Store font variation settings into native Paint

The stored font variation settings is not yet used.

Bug: 361260253
Test: TreeHugger
Flag: com.android.text.flags.typeface_redesign
Change-Id: Idba2995b472c6036077b2cb88abcf59149a89d34
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b866382..68d8ebb 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -41,6 +41,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.text.flags.Flags;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -2000,6 +2001,14 @@
     }
 
     /**
+     * A change ID for new font variation settings management.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = 36)
+    public static final long NEW_FONT_VARIATION_MANAGEMENT = 361260253L;
+
+    /**
      * Sets TrueType or OpenType font variation settings. The settings string is constructed from
      * multiple pairs of axis tag and style values. The axis tag must contain four ASCII characters
      * and must be wrapped with single quotes (U+0027) or double quotes (U+0022). Axis strings that
@@ -2028,12 +2037,16 @@
      * </li>
      * </ul>
      *
+     * <p>Note: If the application that targets API 35 or before, this function mutates the
+     * underlying typeface instance.
+     *
      * @param fontVariationSettings font variation settings. You can pass null or empty string as
      *                              no variation settings.
      *
-     * @return true if the given settings is effective to at least one font file underlying this
-     *         typeface. This function also returns true for empty settings string. Otherwise
-     *         returns false
+     * @return If the application that targets API 36 or later and is running on devices API 36 or
+     *         later, this function always returns true. Otherwise, this function returns true if
+     *         the given settings is effective to at least one font file underlying this typeface.
+     *         This function also returns true for empty settings string. Otherwise returns false.
      *
      * @throws IllegalArgumentException If given string is not a valid font variation settings
      *                                  format
@@ -2042,6 +2055,26 @@
      * @see FontVariationAxis
      */
     public boolean setFontVariationSettings(String fontVariationSettings) {
+        final boolean useFontVariationStore = Flags.typefaceRedesign()
+                && CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT);
+        if (useFontVariationStore) {
+            FontVariationAxis[] axes =
+                    FontVariationAxis.fromFontVariationSettings(fontVariationSettings);
+            if (axes == null) {
+                nSetFontVariationOverride(mNativePaint, 0);
+                mFontVariationSettings = null;
+                return true;
+            }
+
+            long builderPtr = nCreateFontVariationBuilder(axes.length);
+            for (int i = 0; i < axes.length; ++i) {
+                nAddFontVariationToBuilder(builderPtr, axes[i].getOpenTypeTagValue(),
+                        axes[i].getStyleValue());
+            }
+            nSetFontVariationOverride(mNativePaint, builderPtr);
+            mFontVariationSettings = fontVariationSettings;
+            return true;
+        }
         final String settings = TextUtils.nullIfEmpty(fontVariationSettings);
         if (settings == mFontVariationSettings
                 || (settings != null && settings.equals(mFontVariationSettings))) {
@@ -3829,7 +3862,12 @@
     private static native void nSetTextSize(long paintPtr, float textSize);
     @CriticalNative
     private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
-
+    @CriticalNative
+    private static native long nCreateFontVariationBuilder(int size);
+    @CriticalNative
+    private static native void nAddFontVariationToBuilder(long builderPtr, int tag, float value);
+    @CriticalNative
+    private static native void nSetFontVariationOverride(long paintPtr, long builderPtr);
 
     // Following Native methods are kept for old Robolectric JNI signature used by
     // SystemUIGoogleRoboRNGTests
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 708f96e..7eb849f 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -159,6 +159,14 @@
         return SkSamplingOptions(this->filterMode());
     }
 
+    void setVariationOverride(minikin::VariationSettings&& varSettings) {
+        mFontVariationOverride = std::move(varSettings);
+    }
+
+    const minikin::VariationSettings& getFontVariationOverride() const {
+        return mFontVariationOverride;
+    }
+
     // The Java flags (Paint.java) no longer fit into the native apis directly.
     // These methods handle converting to and from them and the native representations
     // in android::Paint.
@@ -179,6 +187,7 @@
     float mLetterSpacing = 0;
     float mWordSpacing = 0;
     std::vector<minikin::FontFeature> mFontFeatureSettings;
+    minikin::VariationSettings mFontVariationOverride;
     uint32_t mMinikinLocaleListId;
     std::optional<minikin::FamilyVariant> mFamilyVariant;
     uint32_t mHyphenEdit = 0;
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index c32ea01..6dfcedc 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -39,6 +39,7 @@
         , mLetterSpacing(paint.mLetterSpacing)
         , mWordSpacing(paint.mWordSpacing)
         , mFontFeatureSettings(paint.mFontFeatureSettings)
+        , mFontVariationOverride(paint.mFontVariationOverride)
         , mMinikinLocaleListId(paint.mMinikinLocaleListId)
         , mFamilyVariant(paint.mFamilyVariant)
         , mHyphenEdit(paint.mHyphenEdit)
@@ -59,6 +60,7 @@
     mLetterSpacing = other.mLetterSpacing;
     mWordSpacing = other.mWordSpacing;
     mFontFeatureSettings = other.mFontFeatureSettings;
+    mFontVariationOverride = other.mFontVariationOverride;
     mMinikinLocaleListId = other.mMinikinLocaleListId;
     mFamilyVariant = other.mFamilyVariant;
     mHyphenEdit = other.mHyphenEdit;
@@ -76,6 +78,7 @@
     return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont &&
            a.mLooper == b.mLooper && a.mLetterSpacing == b.mLetterSpacing &&
            a.mWordSpacing == b.mWordSpacing && a.mFontFeatureSettings == b.mFontFeatureSettings &&
+           a.mFontVariationOverride == b.mFontVariationOverride &&
            a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
            a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
            a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 286f06a..da23792 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -1127,6 +1127,36 @@
         return leftMinikinPaint == rightMinikinPaint;
     }
 
+    struct VariationBuilder {
+        std::vector<minikin::FontVariation> varSettings;
+    };
+
+    static jlong createFontVariationBuilder(CRITICAL_JNI_PARAMS_COMMA jint size) {
+        VariationBuilder* builder = new VariationBuilder();
+        builder->varSettings.reserve(size);
+        return reinterpret_cast<jlong>(builder);
+    }
+
+    static void addFontVariationToBuilder(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag,
+                                          jfloat value) {
+        VariationBuilder* builder = reinterpret_cast<VariationBuilder*>(builderPtr);
+        builder->varSettings.emplace_back(static_cast<minikin::AxisTag>(tag), value);
+    }
+
+    static void setFontVariationOverride(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle,
+                                         jlong builderPtr) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (builderPtr == 0) {
+            paint->setVariationOverride(minikin::VariationSettings());
+            return;
+        }
+
+        VariationBuilder* builder = reinterpret_cast<VariationBuilder*>(builderPtr);
+        paint->setVariationOverride(
+                minikin::VariationSettings(builder->varSettings, false /* sorted */));
+        delete builder;
+    }
+
 }; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
@@ -1235,6 +1265,9 @@
         {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
         {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
         {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+        {"nCreateFontVariationBuilder", "(I)J", (void*)PaintGlue::createFontVariationBuilder},
+        {"nAddFontVariationToBuilder", "(JIF)V", (void*)PaintGlue::addFontVariationToBuilder},
+        {"nSetFontVariationOverride", "(JJ)V", (void*)PaintGlue::setFontVariationOverride},
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {