diff --git a/api/current.txt b/api/current.txt index 6807a7adaaece..415ce7c85a0d6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12740,6 +12740,7 @@ package android.graphics { method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt); method public android.graphics.Paint.FontMetricsInt getFontMetricsInt(); method public float getFontSpacing(); + method public java.lang.String getFontVariationSettings(); method public int getHinting(); method public float getLetterSpacing(); method public android.graphics.MaskFilter getMaskFilter(); @@ -12797,6 +12798,7 @@ package android.graphics { method public void setFilterBitmap(boolean); method public void setFlags(int); method public void setFontFeatureSettings(java.lang.String); + method public void setFontVariationSettings(java.lang.String); method public void setHinting(int); method public void setLetterSpacing(float); method public void setLinearText(boolean); @@ -50036,6 +50038,7 @@ package android.widget { method public int getExtendedPaddingTop(); method public android.text.InputFilter[] getFilters(); method public java.lang.String getFontFeatureSettings(); + method public java.lang.String getFontVariationSettings(); method public boolean getFreezesText(); method public int getGravity(); method public int getHighlightColor(); @@ -50145,6 +50148,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFilters(android.text.InputFilter[]); method public void setFontFeatureSettings(java.lang.String); + method public void setFontVariationSettings(java.lang.String); method protected boolean setFrame(int, int, int, int); method public void setFreezesText(boolean); method public void setGravity(int); diff --git a/api/system-current.txt b/api/system-current.txt index 34b6e80bdfd46..4c194d762e54a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -13303,6 +13303,7 @@ package android.graphics { method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt); method public android.graphics.Paint.FontMetricsInt getFontMetricsInt(); method public float getFontSpacing(); + method public java.lang.String getFontVariationSettings(); method public int getHinting(); method public float getLetterSpacing(); method public android.graphics.MaskFilter getMaskFilter(); @@ -13360,6 +13361,7 @@ package android.graphics { method public void setFilterBitmap(boolean); method public void setFlags(int); method public void setFontFeatureSettings(java.lang.String); + method public void setFontVariationSettings(java.lang.String); method public void setHinting(int); method public void setLetterSpacing(float); method public void setLinearText(boolean); @@ -53803,6 +53805,7 @@ package android.widget { method public int getExtendedPaddingTop(); method public android.text.InputFilter[] getFilters(); method public java.lang.String getFontFeatureSettings(); + method public java.lang.String getFontVariationSettings(); method public boolean getFreezesText(); method public int getGravity(); method public int getHighlightColor(); @@ -53912,6 +53915,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFilters(android.text.InputFilter[]); method public void setFontFeatureSettings(java.lang.String); + method public void setFontVariationSettings(java.lang.String); method protected boolean setFrame(int, int, int, int); method public void setFreezesText(boolean); method public void setGravity(int); diff --git a/api/test-current.txt b/api/test-current.txt index 9abd41b9df40b..244bee3fce395 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12774,6 +12774,7 @@ package android.graphics { method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt); method public android.graphics.Paint.FontMetricsInt getFontMetricsInt(); method public float getFontSpacing(); + method public java.lang.String getFontVariationSettings(); method public int getHinting(); method public float getLetterSpacing(); method public android.graphics.MaskFilter getMaskFilter(); @@ -12831,6 +12832,7 @@ package android.graphics { method public void setFilterBitmap(boolean); method public void setFlags(int); method public void setFontFeatureSettings(java.lang.String); + method public void setFontVariationSettings(java.lang.String); method public void setHinting(int); method public void setLetterSpacing(float); method public void setLinearText(boolean); @@ -50356,6 +50358,7 @@ package android.widget { method public int getExtendedPaddingTop(); method public android.text.InputFilter[] getFilters(); method public java.lang.String getFontFeatureSettings(); + method public java.lang.String getFontVariationSettings(); method public boolean getFreezesText(); method public int getGravity(); method public int getHighlightColor(); @@ -50465,6 +50468,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFilters(android.text.InputFilter[]); method public void setFontFeatureSettings(java.lang.String); + method public void setFontVariationSettings(java.lang.String); method protected boolean setFrame(int, int, int, int); method public void setFreezesText(boolean); method public void setGravity(int); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8ec52bdb12db1..6322615691152 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3457,6 +3457,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTextPaint.getFontFeatureSettings(); } + /** + * Returns the font variation settings. + * + * @return the currently set font variation settings. Returns null if no variation is + * specified. + * + * @see #setFontVariationSettings(String) + * @see Paint#setFontVariationSettings(String) Paint.setFontVariationSettings(String) + */ + @Nullable + public String getFontVariationSettings() { + return mTextPaint.getFontVariationSettings(); + } + /** * Sets the break strategy for breaking paragraphs into lines. The default value for * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for @@ -3563,6 +3577,41 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } + /** + * 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 + * are longer or shorter than four characters, or contain characters outside of U+0020..U+007E + * are invalid. If a specified axis name is not defined in the font, the settings will be + * ignored. + * + *
+     *   textView.setFontVariationSettings("'wdth' 1.0");
+     *   textView.setFontVariationSettings("'AX  ' 1.8, 'FB  ' 2.0");
+     * 
+ * + * @param fontVariationSettings font variation settings. You can pass null or empty string as + * no variation settings. + * + * @see #getFontVariationSettings() + * @see Paint#getFontVariationSettings() Paint.getFontVariationSettings() + */ + public void setFontVariationSettings(@Nullable String fontVariationSettings) { + final String existingSettings = mTextPaint.getFontVariationSettings(); + if (fontVariationSettings == existingSettings + || (fontVariationSettings != null + && fontVariationSettings.equals(existingSettings))) { + return; + } + mTextPaint.setFontVariationSettings(fontVariationSettings); + + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + /** * Sets the text color for all the states (normal, selected, * focused) to be this color. diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 327f142fc319e..cf9441b5c69c6 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -118,6 +118,7 @@ LOCAL_SRC_FILES:= \ android/graphics/ColorFilter.cpp \ android/graphics/DrawFilter.cpp \ android/graphics/FontFamily.cpp \ + android/graphics/FontUtils.cpp \ android/graphics/CreateJavaOutputStreamAdaptor.cpp \ android/graphics/GIFMovie.cpp \ android/graphics/GraphicBuffer.cpp \ diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 0c863fd6eca86..6ec658b9b916e 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -30,6 +30,7 @@ #include #include #include "Utils.h" +#include "FontUtils.h" #include #include @@ -149,16 +150,6 @@ static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, return true; } -static struct { - jmethodID mGet; - jmethodID mSize; -} gListClassInfo; - -static struct { - jfieldID mTag; - jfieldID mStyleValue; -} gAxisClassInfo; - static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr, jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) { NPE_CHECK_RETURN_ZERO(env, font); @@ -167,20 +158,22 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong std::unique_ptr skiaAxes; int skiaAxesLength = 0; if (listOfAxis) { - jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize); + ListHelper list(env, listOfAxis); + jint listSize = list.size(); skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]); skiaAxesLength = listSize; for (jint i = 0; i < listSize; ++i) { - jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i); + jobject axisObject = list.get(i); if (!axisObject) { skiaAxes[i].fTag = 0; skiaAxes[i].fStyleValue = 0; continue; } + AxisHelper axis(env, axisObject); - jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag); - jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue); + jint tag = axis.getTag(); + jfloat stylevalue = axis.getStyleValue(); skiaAxes[i].fTag = tag; skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue); } @@ -291,14 +284,7 @@ int register_android_graphics_FontFamily(JNIEnv* env) int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods, NELEM(gFontFamilyMethods)); - jclass listClass = FindClassOrDie(env, "java/util/List"); - gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); - gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I"); - - jclass axisClass = FindClassOrDie(env, "android/text/FontConfig$Axis"); - gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I"); - gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F"); - + init_FontUtils(env); return err; } diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h new file mode 100644 index 0000000000000..8f44b1e82de8b --- /dev/null +++ b/core/jni/android/graphics/FontUtils.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_GRAPHICS_FONT_UTILS_H_ +#define _ANDROID_GRAPHICS_FONT_UTILS_H_ + +#include + +namespace android { + +// Utility wrapper for java.util.List +class ListHelper { +public: + ListHelper(JNIEnv* env, jobject list) : mEnv(env), mList(list) {} + + jint size() const; + jobject get(jint index) const; + +private: + JNIEnv* mEnv; + jobject mList; +}; + +// Utility wrapper for android.graphics.FontConfig$Axis +class AxisHelper { +public: + AxisHelper(JNIEnv* env, jobject axis) : mEnv(env), mAxis(axis) {} + + jint getTag() const; + jfloat getStyleValue() const; + +private: + JNIEnv* mEnv; + jobject mAxis; +}; + +void init_FontUtils(JNIEnv* env); + +}; // namespace android + +#endif // _ANDROID_GRAPHICS_FONT_UTILS_H_ diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index c920b8d653ab3..0a0fce3ecfabf 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -17,12 +17,14 @@ #include "jni.h" #include "core_jni_helpers.h" +#include "FontUtils.h" #include "GraphicsJNI.h" #include "ScopedPrimitiveArray.h" #include "SkTypeface.h" #include #include #include +#include using namespace android; @@ -40,6 +42,23 @@ static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandl return reinterpret_cast(face); } +static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle, + jobject listOfAxis) { + std::vector variations; + ListHelper list(env, listOfAxis); + for (jint i = 0; i < list.size(); i++) { + jobject axisObject = list.get(i); + if (axisObject == nullptr) { + continue; + } + AxisHelper axis(env, axisObject); + variations.push_back(minikin::FontVariation(axis.getTag(), axis.getStyleValue())); + } + Typeface* baseTypeface = reinterpret_cast(familyHandle); + Typeface* result = Typeface::createFromTypefaceWithVariation(baseTypeface, variations); + return reinterpret_cast(result); +} + static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) { Typeface* family = reinterpret_cast(familyHandle); Typeface* face = Typeface::createWeightAlias(family, weight); @@ -77,6 +96,8 @@ static void Typeface_setDefault(JNIEnv *env, jobject, jlong faceHandle) { static const JNINativeMethod gTypefaceMethods[] = { { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface }, + { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J", + (void*)Typeface_createFromTypefaceWithVariation }, { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias }, { "nativeUnref", "(J)V", (void*)Typeface_unref }, { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 4ec564ac58099..b757842c4f3fb 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -22,6 +22,7 @@ import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; @@ -52,9 +53,12 @@ public class FontListParser { // Note that a well-formed variation contains a four-character tag and a float as styleValue, // with spacers in between. The tag is enclosd either by double quotes or single quotes. @VisibleForTesting - public static FontConfig.Axis[] parseFontVariationSettings(String settings) { - String[] settingList = settings.split(","); + public static ArrayList parseFontVariationSettings(@Nullable String settings) { ArrayList axisList = new ArrayList<>(); + if (settings == null) { + return axisList; + } + String[] settingList = settings.split(","); settingLoop: for (String setting : settingList) { int pos = 0; @@ -98,7 +102,7 @@ public class FontListParser { tagString.charAt(3)); axisList.add(new FontConfig.Axis(tag, styleValue)); } - return axisList.toArray(new FontConfig.Axis[axisList.size()]); + return axisList; } @VisibleForTesting diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 7815ae16e7e2c..4ee0c34139686 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -71,6 +71,7 @@ public class Paint { private LocaleList mLocales; private String mFontFeatureSettings; + private String mFontVariationSettings; private static final Object sCacheLock = new Object(); @@ -1493,6 +1494,37 @@ public class Paint { nSetFontFeatureSettings(mNativePaint, settings); } + /** + * Returns the font variation settings. + * + * @return the paint's currently set font variation settings. Default is null. + * + * @see #setFontVariationSettings(String) + */ + public String getFontVariationSettings() { + return mFontVariationSettings; + } + + /** + * Set font variation settings. + * + * @param settings font variation settings, e.g. "'wdth' 300, 'wght' 1.8" + * + * @see #getFontVariationSettings() + * + * @param settings the font variation settings. You can pass null or empty string as no + * variation settings. + */ + public void setFontVariationSettings(String settings) { + settings = TextUtils.nullIfEmpty(settings); + if (settings == mFontVariationSettings + || (settings != null && settings.equals(mFontVariationSettings))) { + return; + } + mFontVariationSettings = settings; + setTypeface(Typeface.createFromTypefaceWithVariation(mTypeface, settings)); + } + /** * Get the current value of hyphen edit. * diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 2e6a901303156..5531871827a2a 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -32,6 +32,7 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.LruCache; import android.util.SparseArray; +import android.graphics.FontListParser; import com.android.internal.annotations.GuardedBy; @@ -406,6 +407,15 @@ public class Typeface { return typeface; } + /** @hide */ + public static Typeface createFromTypefaceWithVariation(Typeface family, + String fontVariationSettings) { + final long ni = family == null ? 0 : family.native_instance; + ArrayList axes = + FontListParser.parseFontVariationSettings(fontVariationSettings); + return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes)); + } + /** * Returns one of the default typeface objects, based on the specified style * @@ -682,6 +692,8 @@ public class Typeface { } private static native long nativeCreateFromTypeface(long native_instance, int style); + private static native long nativeCreateFromTypefaceWithVariation( + long native_instance, List axes); private static native long nativeCreateWeightAlias(long native_instance, int weight); private static native void nativeUnref(long native_instance); private static native int nativeGetStyle(long native_instance); diff --git a/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java b/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java index 2b4e6c27f193a..c7a46a3697720 100644 --- a/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java +++ b/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java @@ -18,9 +18,8 @@ package android.graphics; import android.test.suitebuilder.annotation.SmallTest; import android.text.FontConfig; -import junit.framework.TestCase; - import java.util.List; +import junit.framework.TestCase; public class VariationParserTest extends TestCase { @@ -28,92 +27,92 @@ public class VariationParserTest extends TestCase { @SmallTest public void testParseFontVariationSetting() { int tag = FontListParser.makeTag('w', 'd', 't', 'h'); - FontConfig.Axis[] axis = FontListParser.parseFontVariationSettings("'wdth' 1"); - assertEquals(tag, axis[0].getTag()); - assertEquals(1.0f, axis[0].getStyleValue()); + List axes = FontListParser.parseFontVariationSettings("'wdth' 1"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(1.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings("\"wdth\" 100"); - assertEquals(tag, axis[0].getTag()); - assertEquals(100.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("\"wdth\" 100"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(100.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings(" 'wdth' 100"); - assertEquals(tag, axis[0].getTag()); - assertEquals(100.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings(" 'wdth' 100"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(100.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings("\t'wdth' 0.5"); - assertEquals(tag, axis[0].getTag()); - assertEquals(0.5f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("\t'wdth' 0.5"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(0.5f, axes.get(0).getStyleValue()); tag = FontListParser.makeTag('A', 'X', ' ', ' '); - axis = FontListParser.parseFontVariationSettings("'AX ' 1"); - assertEquals(tag, axis[0].getTag()); - assertEquals(1.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("'AX ' 1"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(1.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings("'AX '\t1"); - assertEquals(tag, axis[0].getTag()); - assertEquals(1.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("'AX '\t1"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(1.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings("'AX '\n1"); - assertEquals(tag, axis[0].getTag()); - assertEquals(1.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("'AX '\n1"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(1.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings("'AX '\r1"); - assertEquals(tag, axis[0].getTag()); - assertEquals(1.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("'AX '\r1"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(1.0f, axes.get(0).getStyleValue()); - axis = FontListParser.parseFontVariationSettings("'AX '\r\t\n 1"); - assertEquals(tag, axis[0].getTag()); - assertEquals(1.0f, axis[0].getStyleValue()); + axes = FontListParser.parseFontVariationSettings("'AX '\r\t\n 1"); + assertEquals(tag, axes.get(0).getTag()); + assertEquals(1.0f, axes.get(0).getStyleValue()); // Test for invalid input - axis = FontListParser.parseFontVariationSettings(""); - assertEquals(0, axis.length); - axis = FontListParser.parseFontVariationSettings("invalid_form"); - assertEquals(0, axis.length); + axes = FontListParser.parseFontVariationSettings(""); + assertEquals(0, axes.size()); + axes = FontListParser.parseFontVariationSettings("invalid_form"); + assertEquals(0, axes.size()); // Test with invalid tag - axis = FontListParser.parseFontVariationSettings("'' 1"); - assertEquals(0, axis.length); - axis = FontListParser.parseFontVariationSettings("'invalid' 1"); - assertEquals(0, axis.length); + axes = FontListParser.parseFontVariationSettings("'' 1"); + assertEquals(0, axes.size()); + axes = FontListParser.parseFontVariationSettings("'invalid' 1"); + assertEquals(0, axes.size()); // Test with invalid styleValue - axis = FontListParser.parseFontVariationSettings("'wdth' "); - assertEquals(0, axis.length); - axis = FontListParser.parseFontVariationSettings("'wdth' x"); - assertEquals(0, axis.length); - axis = FontListParser.parseFontVariationSettings("'wdth' \t"); - assertEquals(0, axis.length); - axis = FontListParser.parseFontVariationSettings("'wdth' \n\r"); - assertEquals(0, axis.length); + axes = FontListParser.parseFontVariationSettings("'wdth' "); + assertEquals(0, axes.size()); + axes = FontListParser.parseFontVariationSettings("'wdth' x"); + assertEquals(0, axes.size()); + axes = FontListParser.parseFontVariationSettings("'wdth' \t"); + assertEquals(0, axes.size()); + axes = FontListParser.parseFontVariationSettings("'wdth' \n\r"); + assertEquals(0, axes.size()); } @SmallTest public void testParseFontVariationStyleSettings() { - FontConfig.Axis[] axis = + List axes = FontListParser.parseFontVariationSettings("'wdth' 10,'AX '\r1"); int tag1 = FontListParser.makeTag('w', 'd', 't', 'h'); int tag2 = FontListParser.makeTag('A', 'X', ' ', ' '); - assertEquals(tag1, axis[0].getTag()); - assertEquals(10.0f, axis[0].getStyleValue()); - assertEquals(tag2, axis[1].getTag()); - assertEquals(1.0f, axis[1].getStyleValue()); + assertEquals(tag1, axes.get(0).getTag()); + assertEquals(10.0f, axes.get(0).getStyleValue()); + assertEquals(tag2, axes.get(1).getTag()); + assertEquals(1.0f, axes.get(1).getStyleValue()); // Test only spacers are allowed before tag - axis = FontListParser.parseFontVariationSettings(" 'wdth' 10,ab'wdth' 1"); + axes = FontListParser.parseFontVariationSettings(" 'wdth' 10,ab'wdth' 1"); tag1 = FontListParser.makeTag('w', 'd', 't', 'h'); - assertEquals(tag1, axis[0].getTag()); - assertEquals(10.0f, axis[0].getStyleValue()); - assertEquals(1, axis.length); + assertEquals(tag1, axes.get(0).getTag()); + assertEquals(10.0f, axes.get(0).getStyleValue()); + assertEquals(1, axes.size()); } @SmallTest public void testInvalidTagCharacters() { - FontConfig.Axis[] axis = + List axes = FontListParser.parseFontVariationSettings("'\u0000\u0000\u0000\u0000' 10"); - assertEquals(0, axis.length); - axis = FontListParser.parseFontVariationSettings("'\u3042\u3044\u3046\u3048' 10"); - assertEquals(0, axis.length); + assertEquals(0, axes.size()); + axes = FontListParser.parseFontVariationSettings("'\u3042\u3044\u3046\u3048' 10"); + assertEquals(0, axes.size()); } @SmallTest diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index 956f66ed22cbd..6a003794fb283 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -17,7 +17,8 @@ #include "MinikinSkia.h" #include - +#include +#include #include #include @@ -86,6 +87,28 @@ int MinikinFontSkia::GetFontIndex() const { return mTtcIndex; } +minikin::MinikinFont* MinikinFontSkia::createFontWithVariation( + const std::vector& variations) const { + SkFontMgr::FontParameters params; + + int ttcIndex; + SkStreamAsset* stream = mTypeface->openStream(&ttcIndex); + LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed"); + + params.setCollectionIndex(ttcIndex); + std::vector skAxes; + skAxes.resize(variations.size()); + for (size_t i = 0; i < variations.size(); i++) { + skAxes[i].fTag = variations[i].axisTag; + skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value); + } + params.setAxes(skAxes.data(), skAxes.size()); + sk_sp fm(SkFontMgr::RefDefault()); + sk_sp face(fm->createFromStream(stream, params)); + + return new MinikinFontSkia(std::move(face), mFontData, mFontSize, ttcIndex); +} + uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) { uint32_t flags = paint->getFlags(); SkPaint::Hinting hinting = paint->getHinting(); diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h index 3ee916c6e8b11..249b0cbe44de9 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -44,6 +44,8 @@ public: const void* GetFontData() const; size_t GetFontSize() const; int GetFontIndex() const; + minikin::MinikinFont* createFontWithVariation( + const std::vector&) const; static uint32_t packPaintFlags(const SkPaint* paint); static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags); diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 9041b44db849f..b69b0cb29efef 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -62,7 +62,7 @@ Typeface* Typeface::resolveDefault(Typeface* src) { Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) { Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; - if (result != 0) { + if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fFontCollection->Ref(); result->fSkiaStyle = style; @@ -72,10 +72,30 @@ Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) { return result; } +Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, + const std::vector& variations) { + Typeface* resolvedFace = Typeface::resolveDefault(src); + Typeface* result = new Typeface(); + if (result != nullptr) { + result->fFontCollection = + resolvedFace->fFontCollection->createCollectionWithVariation(variations); + if (result->fFontCollection == nullptr) { + // None of passed axes are supported by this collection. + // So we will reuse the same collection with incrementing reference count. + result->fFontCollection = resolvedFace->fFontCollection; + result->fFontCollection->Ref(); + } + result->fSkiaStyle = resolvedFace->fSkiaStyle; + result->fBaseWeight = resolvedFace->fBaseWeight; + resolveStyle(result); + } + return result; +} + Typeface* Typeface::createWeightAlias(Typeface* src, int weight) { Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; - if (result != 0) { + if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fFontCollection->Ref(); result->fSkiaStyle = resolvedFace->fSkiaStyle; diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index 1be630c1e9780..4392ebc36badf 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -43,6 +43,9 @@ struct ANDROID_API Typeface { static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style); + static Typeface* createFromTypefaceWithVariation(Typeface* src, + const std::vector& variations); + static Typeface* createWeightAlias(Typeface* src, int baseweight); static Typeface* createFromFamilies(const std::vector& families);