[skottie] Non-legacy brightness effect

Includes POW intrinsic plumbing.

Change-Id: Ida961718e28822c8559f17f97003f67082dd44cc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/287156
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/include/private/SkVx.h b/include/private/SkVx.h
index cd73ab4..696c6d6 100644
--- a/include/private/SkVx.h
+++ b/include/private/SkVx.h
@@ -280,6 +280,7 @@
 
 SIT Vec<1,T> min(const Vec<1,T>& x, const Vec<1,T>& y) { return std::min(x.val, y.val); }
 SIT Vec<1,T> max(const Vec<1,T>& x, const Vec<1,T>& y) { return std::max(x.val, y.val); }
+SIT Vec<1,T> pow(const Vec<1,T>& x, const Vec<1,T>& y) { return std::pow(x.val, y.val); }
 
 SIT Vec<1,T>  atan(const Vec<1,T>& x) { return std:: atan(x.val); }
 SIT Vec<1,T>  ceil(const Vec<1,T>& x) { return std:: ceil(x.val); }
@@ -314,6 +315,7 @@
 
 SINT Vec<N,T> min(const Vec<N,T>& x, const Vec<N,T>& y) { return join(min(x.lo, y.lo), min(x.hi, y.hi)); }
 SINT Vec<N,T> max(const Vec<N,T>& x, const Vec<N,T>& y) { return join(max(x.lo, y.lo), max(x.hi, y.hi)); }
+SINT Vec<N,T> pow(const Vec<N,T>& x, const Vec<N,T>& y) { return join(pow(x.lo, y.lo), pow(x.hi, y.hi)); }
 
 SINT Vec<N,T>  atan(const Vec<N,T>& x) { return join( atan(x.lo),  atan(x.hi)); }
 SINT Vec<N,T>  ceil(const Vec<N,T>& x) { return join( ceil(x.lo),  ceil(x.hi)); }
@@ -351,6 +353,7 @@
 SINTU Vec<N,M<T>> operator> (U x, const Vec<N,T>& y) { return Vec<N,T>(x) >  y; }
 SINTU Vec<N,T>           min(U x, const Vec<N,T>& y) { return min(Vec<N,T>(x), y); }
 SINTU Vec<N,T>           max(U x, const Vec<N,T>& y) { return max(Vec<N,T>(x), y); }
+SINTU Vec<N,T>           pow(U x, const Vec<N,T>& y) { return pow(Vec<N,T>(x), y); }
 
 // ... and same deal for vector/scalar operations.
 SINTU Vec<N,T>    operator+ (const Vec<N,T>& x, U y) { return x +  Vec<N,T>(y); }
@@ -368,6 +371,7 @@
 SINTU Vec<N,M<T>> operator> (const Vec<N,T>& x, U y) { return x >  Vec<N,T>(y); }
 SINTU Vec<N,T>           min(const Vec<N,T>& x, U y) { return min(x, Vec<N,T>(y)); }
 SINTU Vec<N,T>           max(const Vec<N,T>& x, U y) { return max(x, Vec<N,T>(y)); }
+SINTU Vec<N,T>           pow(const Vec<N,T>& x, U y) { return pow(x, Vec<N,T>(y)); }
 
 // All vector/scalar combinations for mad() with at least one vector.
 SINTU Vec<N,T> mad(U f, const Vec<N,T>& m, const Vec<N,T>& a) { return Vec<N,T>(f)*m + a; }
diff --git a/modules/skottie/src/effects/BrightnessContrastEffect.cpp b/modules/skottie/src/effects/BrightnessContrastEffect.cpp
index 1c3e828..479e14d 100644
--- a/modules/skottie/src/effects/BrightnessContrastEffect.cpp
+++ b/modules/skottie/src/effects/BrightnessContrastEffect.cpp
@@ -101,6 +101,28 @@
 
 #endif
 
+// Brightness transfer function approximation:
+//
+//   f(x) = 1 - (1 - x)^(2^(1.8*B))
+//
+// where B is the normalized [-1..1] brightness value
+//
+// Visualization: https://www.desmos.com/calculator/wuyqa2wtol
+//
+static sk_sp<SkData> make_brightness_coeffs(float brightness) {
+    const float coeff_a = std::pow(2.0f, brightness * 1.8f);
+
+    return SkData::MakeWithCopy(&coeff_a, sizeof(coeff_a));
+}
+
+static constexpr char BRIGHTNESS_EFFECT[] = R"(
+    uniform half a;
+
+    void main(inout half4 color) {
+        color.rgb = 1 - pow(1 - color.rgb, half3(a));
+    }
+)";
+
 class BrightnessContrastAdapter final : public DiscardableAdapterBase<BrightnessContrastAdapter,
                                                                       sksg::ExternalColorFilter> {
 public:
@@ -108,7 +130,9 @@
                               const AnimationBuilder& abuilder,
                               sk_sp<sksg::RenderNode> layer)
         : INHERITED(sksg::ExternalColorFilter::Make(std::move(layer)))
+        , fBrightnessEffect(std::get<0>(SkRuntimeEffect::Make(SkString(BRIGHTNESS_EFFECT))))
         , fContrastEffect(std::get<0>(SkRuntimeEffect::Make(SkString(CONTRAST_EFFECT)))) {
+        SkASSERT(fBrightnessEffect);
         SkASSERT(fContrastEffect);
 
         enum : size_t {
@@ -183,17 +207,22 @@
     }
 
     sk_sp<SkColorFilter> makeCF() const {
-        // TODO: brightness
-        const auto contrast = SkTPin(fContrast, -50.0f, 100.0f) / 100; // [-0.5 .. 1]
+        const auto brightness = SkTPin(fBrightness, -150.0f, 150.0f) / 150, // [-1.0 .. 1]
+                     contrast = SkTPin(fContrast  ,  -50.0f, 100.0f) / 100; // [-0.5 .. 1]
 
-        if (SkScalarNearlyZero(fContrast)) {
-            return nullptr;
-        }
 
-        return fContrastEffect->makeColorFilter(make_contrast_coeffs(contrast));
+        auto b_eff = SkScalarNearlyZero(brightness)
+                   ? nullptr
+                   : fBrightnessEffect->makeColorFilter(make_brightness_coeffs(brightness)),
+             c_eff = SkScalarNearlyZero(fContrast)
+                   ? nullptr
+                   : fContrastEffect->makeColorFilter(make_contrast_coeffs(contrast));
+
+        return SkColorFilters::Compose(std::move(c_eff), std::move(b_eff));
     }
 
-    const sk_sp<SkRuntimeEffect> fContrastEffect;
+    const sk_sp<SkRuntimeEffect> fBrightnessEffect,
+                                   fContrastEffect;
 
     ScalarValue fBrightness = 0,
                 fContrast   = 0,
diff --git a/src/sksl/SkSLByteCode.cpp b/src/sksl/SkSLByteCode.cpp
index e36f554..166e621 100644
--- a/src/sksl/SkSLByteCode.cpp
+++ b/src/sksl/SkSLByteCode.cpp
@@ -156,6 +156,7 @@
         case ByteCodeInstruction::kNotB: printf("notb"); break;
         case ByteCodeInstruction::kOrB: printf("orb"); break;
         VECTOR_MATRIX_DISASSEMBLE(kPop, "pop")
+        VECTOR_DISASSEMBLE(kPow, "pow")
         case ByteCodeInstruction::kPushImmediate: {
             uint32_t v = READ32();
             union { uint32_t u; float f; } pun = { v };
@@ -846,6 +847,8 @@
                 sp -= READ8();
                 continue;
 
+            VECTOR_BINARY_FN(kPow, fFloat, skvx::pow)
+
             case ByteCodeInstruction::kPushImmediate:
                 PUSH(U32(READ32()));
                 continue;
diff --git a/src/sksl/SkSLByteCode.h b/src/sksl/SkSLByteCode.h
index 7c1f1ec..7477558 100644
--- a/src/sksl/SkSLByteCode.h
+++ b/src/sksl/SkSLByteCode.h
@@ -92,6 +92,7 @@
     kNotB,
     kOrB,
     VECTOR_MATRIX(kPop),
+    VECTOR(kPow),
     // Followed by a 32 bit value containing the value to push
     kPushImmediate,
     // Followed by a byte indicating external value to read
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 4415fd5..474ea79 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -49,6 +49,7 @@
         { "fract",   ByteCodeInstruction::kFract },
         { "inverse", ByteCodeInstruction::kInverse2x2 },
         { "length",  SpecialIntrinsic::kLength },
+        { "pow",     ByteCodeInstruction::kPow },
         { "sin",     ByteCodeInstruction::kSin },
         { "sqrt",    ByteCodeInstruction::kSqrt },
         { "tan",     ByteCodeInstruction::kTan },
@@ -283,6 +284,7 @@
         VECTOR_MATRIX_BINARY_OP(kDivideF)
         VECTOR_BINARY_OP(kMultiplyI)
         VECTOR_MATRIX_BINARY_OP(kMultiplyF)
+        VECTOR_BINARY_OP(kPow)
         VECTOR_BINARY_OP(kRemainderF)
         VECTOR_BINARY_OP(kRemainderS)
         VECTOR_BINARY_OP(kRemainderU)
@@ -1013,6 +1015,7 @@
             case ByteCodeInstruction::kATan:
             case ByteCodeInstruction::kCos:
             case ByteCodeInstruction::kFract:
+            case ByteCodeInstruction::kPow:
             case ByteCodeInstruction::kSin:
             case ByteCodeInstruction::kSqrt:
             case ByteCodeInstruction::kTan: