diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp index 097d22e58c..3fdda990b4 100644 --- a/modules/skottie/src/SkottieAdapter.cpp +++ b/modules/skottie/src/SkottieAdapter.cpp @@ -13,6 +13,7 @@ #include "SkMatrix44.h" #include "SkPath.h" #include "SkRRect.h" +#include "SkSGColorFilter.h" #include "SkSGDraw.h" #include "SkSGGradient.h" #include "SkSGGroup.h" @@ -23,6 +24,7 @@ #include "SkSGText.h" #include "SkSGTransform.h" #include "SkSGTrimEffect.h" +#include "SkTableColorFilter.h" #include "SkTo.h" #include "SkottieShaper.h" #include "SkottieValue.h" @@ -433,6 +435,101 @@ void GaussianBlurEffectAdapter::apply() { fBlur->setTileMode(kRepeatEdgeMap[repeat_index]); } + +// Levels color correction effect. +// +// Maps the selected channels from [inBlack...inWhite] to [outBlack, outWhite], +// based on a gamma exponent. +// +// For [i0..i1] -> [o0..o1]: +// +// c' = o0 + (o1 - o0) * ((c - i0) / (i1 - i0)) ^ G +// +// The output is optionally clipped to the output range. +// +// In/out intervals are clampped to [0..1]. Inversion is allowed. +LevelsEffectAdapter::LevelsEffectAdapter(sk_sp child) + : fEffect(sksg::ExternalColorFilter::Make(std::move(child))) { + SkASSERT(fEffect); +} + +LevelsEffectAdapter::~LevelsEffectAdapter() = default; + +void LevelsEffectAdapter::apply() { + enum LottieChannel { + kRGB_Channel = 1, + kR_Channel = 2, + kG_Channel = 3, + kB_Channel = 4, + kA_Channel = 5, + }; + + const auto channel = SkScalarTruncToInt(fChannel); + if (channel < kRGB_Channel || channel > kA_Channel) { + fEffect->setColorFilter(nullptr); + return; + } + + auto in_0 = SkTPin(fInBlack, 0.0f, 1.0f), + in_1 = SkTPin(fInWhite, 0.0f, 1.0f), + out_0 = SkTPin(fOutBlack, 0.0f, 1.0f), + out_1 = SkTPin(fOutWhite, 0.0f, 1.0f), + g = 1 / SkTMax(fGamma, 0.0f); + + float clip[] = {0, 1}; + const auto kLottieDoClip = 1; + if (SkScalarTruncToInt(fClipBlack) == kLottieDoClip) { + const auto idx = fOutBlack <= fOutWhite ? 0 : 1; + clip[idx] = out_0; + } + if (SkScalarTruncToInt(fClipWhite) == kLottieDoClip) { + const auto idx = fOutBlack <= fOutWhite ? 1 : 0; + clip[idx] = out_1; + } + SkASSERT(clip[0] <= clip[1]); + + auto dIn = in_1 - in_0, + dOut = out_1 - out_0; + + if (SkScalarNearlyZero(dIn)) { + // Degenerate dIn == 0 makes the arithmetic below explode. + // + // We could specialize the builder to deal with that case, or we could just + // nudge by epsilon to make it all work. The latter approach is simpler + // and doesn't have any noticeable downsides. + // + // Also nudge in_0 towards 0.5, in case it was sqashed against an extremity. + // This allows for some abrupt transition when the output interval is not + // collapsed, and produces results closer to AE. + static constexpr auto kEpsilon = 2 * SK_ScalarNearlyZero; + dIn += std::copysign(kEpsilon, dIn); + in_0 += std::copysign(kEpsilon, .5f - in_0); + SkASSERT(!SkScalarNearlyZero(dIn)); + } + + uint8_t lut[256]; + + auto t = -in_0 / dIn, + dT = 1 / 255.0f / dIn; + + // TODO: is linear gamma common-enough to warrant a fast path? + for (size_t i = 0; i < 256; ++i) { + const auto out = out_0 + dOut * std::pow(std::max(t, 0.0f), g); + SkASSERT(!SkScalarIsNaN(out)); + + lut[i] = static_cast(std::round(SkTPin(out, clip[0], clip[1]) * 255)); + + t += dT; + } + + fEffect->setColorFilter(SkTableColorFilter::MakeARGB( + channel == kA_Channel ? lut : nullptr, + channel == kR_Channel || channel == kRGB_Channel ? lut : nullptr, + channel == kG_Channel || channel == kRGB_Channel ? lut : nullptr, + channel == kB_Channel || channel == kRGB_Channel ? lut : nullptr + )); +} + TextAdapter::TextAdapter(sk_sp root) : fRoot(std::move(root)) , fTextNode(sksg::TextBlob::Make()) diff --git a/modules/skottie/src/SkottieAdapter.h b/modules/skottie/src/SkottieAdapter.h index 7fcd9ef79a..b5e0cb99bd 100644 --- a/modules/skottie/src/SkottieAdapter.h +++ b/modules/skottie/src/SkottieAdapter.h @@ -19,6 +19,7 @@ class BlurImageFilter; class Color; class Draw; class DropShadowImageFilter; +class ExternalColorFilter; class Gradient; class Group; class LinearGradient; @@ -329,6 +330,30 @@ private: const sk_sp fBlur; }; +class LevelsEffectAdapter final : public SkNVRefCnt { +public: + explicit LevelsEffectAdapter(sk_sp child); + ~LevelsEffectAdapter(); + + // 1: RGB, 2: R, 3: G, 4: B, 5: A + ADAPTER_PROPERTY( Channel, SkScalar, 1) + ADAPTER_PROPERTY( InBlack, SkScalar, 0) + ADAPTER_PROPERTY( InWhite, SkScalar, 1) + ADAPTER_PROPERTY( OutBlack, SkScalar, 0) + ADAPTER_PROPERTY( OutWhite, SkScalar, 1) + ADAPTER_PROPERTY( Gamma, SkScalar, 1) + // 1: clip, 2,3: don't clip + ADAPTER_PROPERTY(ClipBlack, SkScalar, 1) + ADAPTER_PROPERTY(ClipWhite, SkScalar, 1) + + const sk_sp& root() const { return fEffect; } + +private: + void apply(); + + sk_sp fEffect; +}; + class TextAdapter final : public SkNVRefCnt { public: explicit TextAdapter(sk_sp root); diff --git a/modules/skottie/src/SkottieLayerEffect.cpp b/modules/skottie/src/SkottieLayerEffect.cpp index 24d54d2896..5cc3a6583d 100644 --- a/modules/skottie/src/SkottieLayerEffect.cpp +++ b/modules/skottie/src/SkottieLayerEffect.cpp @@ -322,6 +322,81 @@ sk_sp AttachGaussianBlurLayerEffect(const skjson::ArrayValue& return sksg::ImageFilterEffect::Make(std::move(layer), std::move(blur_effect)); } +sk_sp AttachLevelsLayerEffect(const skjson::ArrayValue& jprops, + const AnimationBuilder* abuilder, + AnimatorScope* ascope, + sk_sp layer) { + enum : size_t { + kChannel_Index = 0, + // ??? = 1, + kInputBlack_Index = 2, + kInputWhite_Index = 3, + kGamma_Index = 4, + kOutputBlack_Index = 5, + kOutputWhite_Index = 6, + kClipToOutBlack_Index = 7, + kClipToOutWhite_Index = 8, + + kMax_Index = kClipToOutWhite_Index, + }; + + if (jprops.size() <= kMax_Index) { + return nullptr; + } + + const skjson::ObjectValue* channel_prop = jprops[ kChannel_Index]; + const skjson::ObjectValue* iblack_prop = jprops[ kInputBlack_Index]; + const skjson::ObjectValue* iwhite_prop = jprops[ kInputWhite_Index]; + const skjson::ObjectValue* gamma_prop = jprops[ kGamma_Index]; + const skjson::ObjectValue* oblack_prop = jprops[ kOutputBlack_Index]; + const skjson::ObjectValue* owhite_prop = jprops[ kOutputWhite_Index]; + const skjson::ObjectValue* clip_black_prop = jprops[kClipToOutBlack_Index]; + const skjson::ObjectValue* clip_white_prop = jprops[kClipToOutWhite_Index]; + + if (!channel_prop || !iblack_prop || !iwhite_prop || !gamma_prop || + !oblack_prop || !owhite_prop || !clip_black_prop || !clip_white_prop) { + return nullptr; + } + + auto adapter = sk_make_sp(std::move(layer)); + + abuilder->bindProperty((*channel_prop)["v"], ascope, + [adapter](const ScalarValue& channel) { + adapter->setChannel(channel); + }); + abuilder->bindProperty((*iblack_prop)["v"], ascope, + [adapter](const ScalarValue& ib) { + adapter->setInBlack(ib); + }); + abuilder->bindProperty((*iwhite_prop)["v"], ascope, + [adapter](const ScalarValue& iw) { + adapter->setInWhite(iw); + }); + abuilder->bindProperty((*oblack_prop)["v"], ascope, + [adapter](const ScalarValue& ob) { + adapter->setOutBlack(ob); + }); + abuilder->bindProperty((*owhite_prop)["v"], ascope, + [adapter](const ScalarValue& ow) { + adapter->setOutWhite(ow); + }); + abuilder->bindProperty((*gamma_prop)["v"], ascope, + [adapter](const ScalarValue& g) { + adapter->setGamma(g); + }); + + abuilder->bindProperty((*clip_black_prop)["v"], ascope, + [adapter](const ScalarValue& cb) { + adapter->setClipBlack(cb); + }); + abuilder->bindProperty((*clip_white_prop)["v"], ascope, + [adapter](const ScalarValue& cw) { + adapter->setClipWhite(cw); + }); + + return adapter->root(); +} + using EffectBuilderT = sk_sp (*)(const skjson::ArrayValue&, const AnimationBuilder*, AnimatorScope*, @@ -359,10 +434,12 @@ EffectBuilderT FindEffectBuilder(const AnimationBuilder* abuilder, // Try a name-based lookup. if (const skjson::StringValue* mn = jeffect["mn"]) { - // Just gradient ramp for now. if (!strcmp(mn->begin(), "ADBE Ramp")) { return AttachGradientLayerEffect; } + if (!strcmp(mn->begin(), "ADBE Easy Levels2")) { + return AttachLevelsLayerEffect; + } } abuilder->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty); diff --git a/modules/skottie/tests/levels-effect.json b/modules/skottie/tests/levels-effect.json new file mode 100644 index 0000000000..1f5d7aa042 --- /dev/null +++ b/modules/skottie/tests/levels-effect.json @@ -0,0 +1 @@ +{"v":"5.5.0","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"ColorCorrection","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":1,"nm":"White Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Gradient Ramp","np":10,"mn":"ADBE Ramp","ix":1,"en":1,"ef":[{"ty":3,"nm":"Start of Ramp","mn":"ADBE Ramp-0001","ix":1,"v":{"a":0,"k":[50,50],"ix":1}},{"ty":2,"nm":"Start Color","mn":"ADBE Ramp-0002","ix":2,"v":{"a":0,"k":[1,0,0,1],"ix":2}},{"ty":3,"nm":"End of Ramp","mn":"ADBE Ramp-0003","ix":3,"v":{"a":0,"k":[50,100],"ix":3}},{"ty":2,"nm":"End Color","mn":"ADBE Ramp-0004","ix":4,"v":{"a":0,"k":[0,1,0,1],"ix":4}},{"ty":7,"nm":"Ramp Shape","mn":"ADBE Ramp-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Ramp Scatter","mn":"ADBE Ramp-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Blend With Original","mn":"ADBE Ramp-0007","ix":7,"v":{"a":0,"k":0,"ix":7}},{"ty":6,"nm":"","mn":"ADBE Ramp-0008","ix":8,"v":0}]}],"sw":100,"sh":100,"sc":"#ffffff","ip":0,"op":300,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"gamma 0","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[70,70,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"gamma 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[190,70,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"gamma 2","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[310,70,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2.5,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"gamma 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[430,70,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":5,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"indeg 0","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[70,190,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0.502,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"indeg 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[190,190,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.502,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0.502,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0.502,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"indeg 2","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[310,190,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0.502,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"indeg 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[430,190,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0.502,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":0.502,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":0,"nm":"inv 0","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[70,310,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.502,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0.502,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"inv 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[190,310,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0.502,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":0.502,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":0,"nm":"inv 2","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[310,310,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.502,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":0.502,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"inv 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[430,310,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.737,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0.285,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":3,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":2,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":2,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":0,"nm":"clip 0","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[70,430,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.502,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0.502,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":1,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":1,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":0,"nm":"clip 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[190,430,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0.502,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":0.502,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":1,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":1,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":0,"nm":"clip 2","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[310,430,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.502,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":2,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":0.502,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":1,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":1,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":0,"nm":"clip 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[430,430,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Levels","np":11,"mn":"ADBE Easy Levels2","ix":1,"en":1,"ef":[{"ty":7,"nm":"Channel:","mn":"ADBE Easy Levels2-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{},{"ty":0,"nm":"Input Black","mn":"ADBE Easy Levels2-0003","ix":3,"v":{"a":0,"k":0.737,"ix":3}},{"ty":0,"nm":"Input White","mn":"ADBE Easy Levels2-0004","ix":4,"v":{"a":0,"k":0.285,"ix":4}},{"ty":0,"nm":"Gamma","mn":"ADBE Easy Levels2-0005","ix":5,"v":{"a":0,"k":3,"ix":5}},{"ty":0,"nm":"Output Black","mn":"ADBE Easy Levels2-0006","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Output White","mn":"ADBE Easy Levels2-0007","ix":7,"v":{"a":0,"k":1,"ix":7}},{"ty":7,"nm":"Clip To Output Black","mn":"ADBE Easy Levels2-0008","ix":8,"v":{"a":0,"k":1,"ix":8}},{"ty":7,"nm":"Clip To Output White","mn":"ADBE Easy Levels2-0009","ix":9,"v":{"a":0,"k":1,"ix":9}}]}],"w":500,"h":500,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":"bg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#ffffff","ip":0,"op":300,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/modules/sksg/include/SkSGColorFilter.h b/modules/sksg/include/SkSGColorFilter.h index 0029b31939..7c0295885f 100644 --- a/modules/sksg/include/SkSGColorFilter.h +++ b/modules/sksg/include/SkSGColorFilter.h @@ -40,6 +40,30 @@ private: typedef EffectNode INHERITED; }; +/** + * Wrapper for externally-managed SkColorFilters. + * + * Allows attaching non-sksg color filters to the render tree. + */ +class ExternalColorFilter final : public EffectNode { +public: + static sk_sp Make(sk_sp child); + + ~ExternalColorFilter() override; + + SG_ATTRIBUTE(ColorFilter, sk_sp, fColorFilter) + +protected: + void onRender(SkCanvas*, const RenderContext*) const override; + +private: + explicit ExternalColorFilter(sk_sp); + + sk_sp fColorFilter; + + using INHERITED = EffectNode; +}; + /** * Concrete SkModeColorFilter Effect node. */ diff --git a/modules/sksg/src/SkSGColorFilter.cpp b/modules/sksg/src/SkSGColorFilter.cpp index c6b876cfa6..f247f058ea 100644 --- a/modules/sksg/src/SkSGColorFilter.cpp +++ b/modules/sksg/src/SkSGColorFilter.cpp @@ -38,6 +38,21 @@ SkRect ColorFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm return this->INHERITED::onRevalidate(ic, ctm); } +sk_sp ExternalColorFilter::Make(sk_sp child) { + return child ? sk_sp(new ExternalColorFilter(std::move(child))) + : nullptr; +} + +ExternalColorFilter::ExternalColorFilter(sk_sp child) : INHERITED(std::move(child)) {} + +ExternalColorFilter::~ExternalColorFilter() = default; + +void ExternalColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const { + const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter); + + this->INHERITED::onRender(canvas, local_ctx); +} + sk_sp ModeColorFilter::Make(sk_sp child, sk_sp color, SkBlendMode mode) { return (child && color) ? sk_sp(new ModeColorFilter(std::move(child),