From ba3028c1fc9fca2d45acc841557da2c9a83923bf Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Wed, 30 Nov 2016 14:04:21 -0800 Subject: [PATCH] Introduce set/getFontVariationSettings. This CL enables developers to specify axis values to the underlying font collection. The specification of the font variation settings is the same as the CSS font-variation-settings attribute in CSS working draft as of 2016-11-30. Code example: Here is an example to set width 100 and weight 1.5. TextView tv = (TextView) findViewById(R.id.textView); tv.setFontVariationSettings("'wdth' 100, 'wght' 1.5"); Bug: 33062398 Test: Manually done. Ran FrameworksGraphicsTests, CtsGraphicsTestCases and CtsWidgetTestCases Change-Id: I249d464f8cdaa56017a987588b94ed685aadeb58 --- api/current.txt | 4 + api/system-current.txt | 4 + api/test-current.txt | 4 + core/java/android/widget/TextView.java | 49 ++++++++ core/jni/Android.mk | 1 + core/jni/android/graphics/FontFamily.cpp | 30 ++--- core/jni/android/graphics/FontUtils.cpp | 63 ++++++++++ core/jni/android/graphics/FontUtils.h | 54 +++++++++ core/jni/android/graphics/Typeface.cpp | 21 ++++ .../java/android/graphics/FontListParser.java | 10 +- graphics/java/android/graphics/Paint.java | 32 +++++ graphics/java/android/graphics/Typeface.java | 12 ++ .../android/graphics/VariationParserTest.java | 113 +++++++++--------- libs/hwui/hwui/MinikinSkia.cpp | 25 +++- libs/hwui/hwui/MinikinSkia.h | 2 + libs/hwui/hwui/Typeface.cpp | 24 +++- libs/hwui/hwui/Typeface.h | 3 + 17 files changed, 367 insertions(+), 84 deletions(-) create mode 100644 core/jni/android/graphics/FontUtils.cpp create mode 100644 core/jni/android/graphics/FontUtils.h diff --git a/api/current.txt b/api/current.txt index aad28af6b8baf..9a08b812a39f4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12492,6 +12492,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(); @@ -12549,6 +12550,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); @@ -49176,6 +49178,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(); @@ -49281,6 +49284,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 7bd70125a2f61..bc3d19dceebd9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12996,6 +12996,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(); @@ -13053,6 +13054,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); @@ -52760,6 +52762,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(); @@ -52865,6 +52868,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 e52f69cc6b233..f140d6d32af8f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12523,6 +12523,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(); @@ -12580,6 +12581,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); @@ -49478,6 +49480,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(); @@ -49583,6 +49586,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 1ddf53d8b19a8..4b48501cb5030 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3243,6 +3243,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 @@ -3349,6 +3363,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 29483c78b9d1f..ab784a6e6261f 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -117,6 +117,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 adee2fd717b76..685c93d55924f 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 @@ -143,16 +144,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); @@ -161,20 +152,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); } @@ -273,14 +266,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/graphics/FontListParser$Axis"); - gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I"); - gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F"); - + init_FontUtils(env); return err; } diff --git a/core/jni/android/graphics/FontUtils.cpp b/core/jni/android/graphics/FontUtils.cpp new file mode 100644 index 0000000000000..11c2d29ec03c7 --- /dev/null +++ b/core/jni/android/graphics/FontUtils.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "FontUtils.h" + +#include "JNIHelp.h" +#include + +namespace android { +namespace { + +static struct { + jmethodID mGet; + jmethodID mSize; +} gListClassInfo; + +static struct { + jfieldID mTag; + jfieldID mStyleValue; +} gAxisClassInfo; + +} // namespace + +jint ListHelper::size() const { + return mEnv->CallIntMethod(mList, gListClassInfo.mSize); +} + +jobject ListHelper::get(jint index) const { + return mEnv->CallObjectMethod(mList, gListClassInfo.mGet, index); +} + +jint AxisHelper::getTag() const { + return mEnv->GetIntField(mAxis, gAxisClassInfo.mTag); +} + +jfloat AxisHelper::getStyleValue() const { + return mEnv->GetFloatField(mAxis, gAxisClassInfo.mStyleValue); +} + +void init_FontUtils(JNIEnv* env) { + 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/graphics/FontListParser$Axis"); + gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I"); + gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F"); +} + +} // namespace android diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h new file mode 100644 index 0000000000000..6fbd5e32afde8 --- /dev/null +++ b/core/jni/android/graphics/FontUtils.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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.FontListParser$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 9490436d4f046..5b53296d388c0 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -21,6 +21,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; @@ -104,9 +105,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 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; @@ -150,7 +154,7 @@ public class FontListParser { tagString.charAt(3)); axisList.add(new Axis(tag, styleValue)); } - return axisList.toArray(new 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 a8c1690e191ac..166ef1b3113e8 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -21,6 +21,7 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.LruCache; import android.util.SparseArray; +import android.graphics.FontListParser; import org.xmlpull.v1.XmlPullParserException; @@ -171,6 +172,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 * @@ -443,6 +453,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 d046c1103efc1..23de416040f01 100644 --- a/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java +++ b/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java @@ -17,6 +17,7 @@ package android.graphics; import android.test.suitebuilder.annotation.SmallTest; +import java.util.List; import junit.framework.TestCase; @@ -25,92 +26,92 @@ public class VariationParserTest extends TestCase { @SmallTest public void testParseFontVariationSetting() { int tag = FontListParser.makeTag('w', 'd', 't', 'h'); - FontListParser.Axis[] axis = FontListParser.parseFontVariationSettings("'wdth' 1"); - assertEquals(tag, axis[0].tag); - assertEquals(1.0f, axis[0].styleValue); + List axes = FontListParser.parseFontVariationSettings("'wdth' 1"); + assertEquals(tag, axes.get(0).tag); + assertEquals(1.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings("\"wdth\" 100"); - assertEquals(tag, axis[0].tag); - assertEquals(100.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("\"wdth\" 100"); + assertEquals(tag, axes.get(0).tag); + assertEquals(100.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings(" 'wdth' 100"); - assertEquals(tag, axis[0].tag); - assertEquals(100.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings(" 'wdth' 100"); + assertEquals(tag, axes.get(0).tag); + assertEquals(100.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings("\t'wdth' 0.5"); - assertEquals(tag, axis[0].tag); - assertEquals(0.5f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("\t'wdth' 0.5"); + assertEquals(tag, axes.get(0).tag); + assertEquals(0.5f, axes.get(0).styleValue); tag = FontListParser.makeTag('A', 'X', ' ', ' '); - axis = FontListParser.parseFontVariationSettings("'AX ' 1"); - assertEquals(tag, axis[0].tag); - assertEquals(1.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("'AX ' 1"); + assertEquals(tag, axes.get(0).tag); + assertEquals(1.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings("'AX '\t1"); - assertEquals(tag, axis[0].tag); - assertEquals(1.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("'AX '\t1"); + assertEquals(tag, axes.get(0).tag); + assertEquals(1.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings("'AX '\n1"); - assertEquals(tag, axis[0].tag); - assertEquals(1.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("'AX '\n1"); + assertEquals(tag, axes.get(0).tag); + assertEquals(1.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings("'AX '\r1"); - assertEquals(tag, axis[0].tag); - assertEquals(1.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("'AX '\r1"); + assertEquals(tag, axes.get(0).tag); + assertEquals(1.0f, axes.get(0).styleValue); - axis = FontListParser.parseFontVariationSettings("'AX '\r\t\n 1"); - assertEquals(tag, axis[0].tag); - assertEquals(1.0f, axis[0].styleValue); + axes = FontListParser.parseFontVariationSettings("'AX '\r\t\n 1"); + assertEquals(tag, axes.get(0).tag); + assertEquals(1.0f, axes.get(0).styleValue); // 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() { - FontListParser.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].tag); - assertEquals(10.0f, axis[0].styleValue); - assertEquals(tag2, axis[1].tag); - assertEquals(1.0f, axis[1].styleValue); + assertEquals(tag1, axes.get(0).tag); + assertEquals(10.0f, axes.get(0).styleValue); + assertEquals(tag2, axes.get(1).tag); + assertEquals(1.0f, axes.get(1).styleValue); // 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].tag); - assertEquals(10.0f, axis[0].styleValue); - assertEquals(1, axis.length); + assertEquals(tag1, axes.get(0).tag); + assertEquals(10.0f, axes.get(0).styleValue); + assertEquals(1, axes.size()); } @SmallTest public void testInvalidTagCharacters() { - FontListParser.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);