Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 713b869a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Expose Typeface creation APIs with ttc and font variation."

parents 7105dd8b 20e5d917
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -106,8 +107,17 @@ public final class FontConfig implements Parcelable {
        private final float mStyleValue;

        public Axis(int tag, float styleValue) {
            this.mTag = tag;
            this.mStyleValue = styleValue;
            mTag = tag;
            mStyleValue = styleValue;
        }

        /** @hide */
        public Axis(@NonNull String tagString, float styleValue) {
            if (!FontListParser.isValidTag(tagString)) {
                throw new IllegalArgumentException("Invalid tag pattern: " + tagString);
            }
            mTag = FontListParser.makeTag(tagString);
            mStyleValue = styleValue;
        }

        /**
+65 −70
Original line number Diff line number Diff line
@@ -34,12 +34,16 @@

#include <hwui/MinikinSkia.h>
#include <hwui/Typeface.h>
#include <utils/FatVector.h>
#include <minikin/FontFamily.h>

#include <memory>

namespace android {

// Must be same with Java constant in Typeface.Builder. See Typeface.java
constexpr jint RESOLVE_BY_FONT_TABLE = -1;

struct NativeFamilyBuilder {
    uint32_t langId;
    int variant;
@@ -81,24 +85,53 @@ static void FontFamily_unref(jlong familyPtr) {
    delete family;
}

static void addSkTypeface(jlong builderPtr, sk_sp<SkTypeface> face, const void* fontData,
        size_t fontSize, int ttcIndex, jint givenWeight, jboolean givenItalic) {
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
        jint givenWeight, jint givenItalic) {
    uirenderer::FatVector<SkFontMgr::FontParameters::Axis, 2> skiaAxes;
    for (const auto& axis : builder->axes) {
        skiaAxes.emplace_back(SkFontMgr::FontParameters::Axis{axis.axisTag, axis.value});
    }

    const size_t fontSize = data->size();
    const void* fontPtr = data->data();
    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));

    SkFontMgr::FontParameters params;
    params.setCollectionIndex(ttcIndex);
    params.setAxes(skiaAxes.data(), skiaAxes.size());

    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
    sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params));
    if (face == NULL) {
        ALOGE("addFont failed to create font, invalid request");
        builder->axes.clear();
        return false;
    }
    std::shared_ptr<minikin::MinikinFont> minikinFont =
            std::make_shared<MinikinFontSkia>(std::move(face), fontData, fontSize, ttcIndex,
                    std::vector<minikin::FontVariation>());
    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
                    builder->axes);

    int weight = givenWeight / 100;
    bool italic = givenItalic;
    if (weight == 0) {
        if (!minikin::FontFamily::analyzeStyle(minikinFont, &weight, &italic)) {
    bool italic = givenItalic == 1;
    if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) {
        int os2Weight;
        bool os2Italic;
        if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) {
            ALOGE("analyzeStyle failed. Using default style");
            weight = 4;
            italic = false;
            os2Weight = 4;
            os2Italic = false;
        }
        if (givenWeight == RESOLVE_BY_FONT_TABLE) {
            weight = os2Weight;
        }
        if (givenItalic == RESOLVE_BY_FONT_TABLE) {
            italic = os2Italic;
        }
    }

    builder->fonts.push_back(minikin::Font(
            std::move(minikinFont), minikin::FontStyle(weight, italic)));
    builder->fonts.push_back(minikin::Font(minikinFont, minikin::FontStyle(weight, italic)));
    builder->axes.clear();
    return true;
}

static void release_global_ref(const void* /*data*/, void* context) {
@@ -125,80 +158,47 @@ static void release_global_ref(const void* /*data*/, void* context) {
}

static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf,
        jint ttcIndex) {
        jint ttcIndex, jint weight, jint isItalic) {
    NPE_CHECK_RETURN_ZERO(env, bytebuf);
    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
    const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
    if (fontPtr == NULL) {
        ALOGE("addFont failed to create font, buffer invalid");
        builder->axes.clear();
        return false;
    }
    jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
    if (fontSize < 0) {
        ALOGE("addFont failed to create font, buffer size invalid");
        builder->axes.clear();
        return false;
    }
    jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
            release_global_ref, reinterpret_cast<void*>(fontRef)));
    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));

    SkFontMgr::FontParameters params;
    params.setCollectionIndex(ttcIndex);

    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
    sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params));
    if (face == NULL) {
        ALOGE("addFont failed to create font");
        return false;
    }
    addSkTypeface(builderPtr, std::move(face), fontPtr, (size_t)fontSize, ttcIndex, 0, false);
    return true;
    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}

static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr,
        jobject font, jint ttcIndex, jint weight, jboolean isItalic) {
        jobject font, jint ttcIndex, jint weight, jint isItalic) {
    NPE_CHECK_RETURN_ZERO(env, font);

    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);

    // Declare axis native type.
    std::vector<SkFontMgr::FontParameters::Axis> skiaAxes;
    skiaAxes.reserve(builder->axes.size());
    for (const minikin::FontVariation& minikinAxis : builder->axes) {
        skiaAxes.push_back({minikinAxis.axisTag, minikinAxis.value});
    }

    const void* fontPtr = env->GetDirectBufferAddress(font);
    if (fontPtr == NULL) {
        ALOGE("addFont failed to create font, buffer invalid");
        builder->axes.clear();
        return false;
    }
    jlong fontSize = env->GetDirectBufferCapacity(font);
    if (fontSize < 0) {
        ALOGE("addFont failed to create font, buffer size invalid");
        builder->axes.clear();
        return false;
    }
    jobject fontRef = MakeGlobalRefOrDie(env, font);
    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
            release_global_ref, reinterpret_cast<void*>(fontRef)));
    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));

    SkFontMgr::FontParameters params;
    params.setCollectionIndex(ttcIndex);
    params.setAxes(skiaAxes.data(), skiaAxes.size());

    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
    sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params));
    if (face == NULL) {
        ALOGE("addFont failed to create font, invalid request");
        return false;
    }
    std::shared_ptr<minikin::MinikinFont> minikinFont =
            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
                    std::vector<minikin::FontVariation>());
    builder->fonts.push_back(minikin::Font(std::move(minikinFont),
            minikin::FontStyle(weight / 100, isItalic)));
    return true;
    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}

static void releaseAsset(const void* ptr, void* context) {
@@ -206,18 +206,21 @@ static void releaseAsset(const void* ptr, void* context) {
}

static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr,
        jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint weight,
        jboolean isItalic) {
        jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex,
        jint weight, jint isItalic) {
    NPE_CHECK_RETURN_ZERO(env, jassetMgr);
    NPE_CHECK_RETURN_ZERO(env, jpath);

    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
    AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
    if (NULL == mgr) {
        builder->axes.clear();
        return false;
    }

    ScopedUtfChars str(env, jpath);
    if (str.c_str() == nullptr) {
        builder->axes.clear();
        return false;
    }

@@ -230,27 +233,19 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b
    }

    if (NULL == asset) {
        builder->axes.clear();
        return false;
    }

    const void* buf = asset->getBuffer(false);
    if (NULL == buf) {
        delete asset;
        builder->axes.clear();
        return false;
    }

    size_t bufSize = asset->getLength();
    sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset));
    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));

    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
    sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), SkFontMgr::FontParameters()));
    if (face == NULL) {
        ALOGE("addFontFromAsset failed to create font %s", str.c_str());
        return false;
    }

    addSkTypeface(builderPtr, std::move(face), buf, bufSize, 0 /* ttc index */, weight, isItalic);
    addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
    return true;
}

@@ -266,10 +261,10 @@ static const JNINativeMethod gFontFamilyMethods[] = {
    { "nCreateFamily",         "(J)J", (void*)FontFamily_create },
    { "nAbort",                "(J)V", (void*)FontFamily_abort },
    { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
    { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
    { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;IIZ)Z",
    { "nAddFont",              "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
    { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;III)Z",
            (void*)FontFamily_addFontWeightStyle },
    { "nAddFontFromAssetManager",    "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIZ)Z",
    { "nAddFontFromAssetManager",    "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z",
            (void*)FontFamily_addFontFromAssetManager },
    { "nAddAxisValue",         "(JIF)V", (void*)FontFamily_addAxisValue },
};
+8 −8
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ public class VariationParserTest extends TestCase {

    @SmallTest
    public void testParseFontVariationSetting() {
        int tag = FontListParser.makeTag('w', 'd', 't', 'h');
        int tag = FontListParser.makeTag("wdth");
        List<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 1");
        assertEquals(tag, axes.get(0).getTag());
        assertEquals(1.0f, axes.get(0).getStyleValue());
@@ -45,7 +45,7 @@ public class VariationParserTest extends TestCase {
        assertEquals(tag, axes.get(0).getTag());
        assertEquals(0.5f, axes.get(0).getStyleValue());

        tag = FontListParser.makeTag('A', 'X', ' ', ' ');
        tag = FontListParser.makeTag("AX  ");
        axes = FontListParser.parseFontVariationSettings("'AX  ' 1");
        assertEquals(tag, axes.get(0).getTag());
        assertEquals(1.0f, axes.get(0).getStyleValue());
@@ -93,8 +93,8 @@ public class VariationParserTest extends TestCase {
    public void testParseFontVariationStyleSettings() {
        List<FontConfig.Axis> axes =
                FontListParser.parseFontVariationSettings("'wdth' 10,'AX  '\r1");
        int tag1 = FontListParser.makeTag('w', 'd', 't', 'h');
        int tag2 = FontListParser.makeTag('A', 'X', ' ', ' ');
        int tag1 = FontListParser.makeTag("wdth");
        int tag2 = FontListParser.makeTag("AX  ");
        assertEquals(tag1, axes.get(0).getTag());
        assertEquals(10.0f, axes.get(0).getStyleValue());
        assertEquals(tag2, axes.get(1).getTag());
@@ -102,7 +102,7 @@ public class VariationParserTest extends TestCase {

        // Test only spacers are allowed before tag
        axes = FontListParser.parseFontVariationSettings("     'wdth' 10,ab'wdth' 1");
        tag1 = FontListParser.makeTag('w', 'd', 't', 'h');
        tag1 = FontListParser.makeTag("wdth");
        assertEquals(tag1, axes.get(0).getTag());
        assertEquals(10.0f, axes.get(0).getStyleValue());
        assertEquals(1, axes.size());
@@ -119,8 +119,8 @@ public class VariationParserTest extends TestCase {

    @SmallTest
    public void testMakeTag() {
      assertEquals(0x77647468, FontListParser.makeTag('w', 'd', 't', 'h'));
      assertEquals(0x41582020, FontListParser.makeTag('A', 'X', ' ', ' '));
      assertEquals(0x20202020, FontListParser.makeTag(' ', ' ', ' ', ' '));
      assertEquals(0x77647468, FontListParser.makeTag("wdth"));
      assertEquals(0x41582020, FontListParser.makeTag("AX  "));
      assertEquals(0x20202020, FontListParser.makeTag("    "));
    }
}
+30 −12
Original line number Diff line number Diff line
@@ -81,7 +81,8 @@ public class FontFamily {
        }
    }

    public boolean addFont(String path, int ttcIndex) {
    public boolean addFont(String path, int ttcIndex, FontConfig.Axis[] axes, int weight,
            int italic) {
        if (mBuilderPtr == 0) {
            throw new IllegalStateException("Unable to call addFont after freezing.");
        }
@@ -89,22 +90,29 @@ public class FontFamily {
            FileChannel fileChannel = file.getChannel();
            long fontSize = fileChannel.size();
            ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
            return nAddFont(mBuilderPtr, fontBuffer, ttcIndex);
            if (axes != null) {
                for (FontConfig.Axis axis : axes) {
                    nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue());
                }
            }
            return nAddFont(mBuilderPtr, fontBuffer, ttcIndex, weight, italic);
        } catch (IOException e) {
            Log.e(TAG, "Error mapping font file " + path);
            return false;
        }
    }

    public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes,
            int weight, boolean style) {
    public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes,
            int weight, int italic) {
        if (mBuilderPtr == 0) {
            throw new IllegalStateException("Unable to call addFontWeightStyle after freezing.");
        }
        if (axes != null) {
            for (FontConfig.Axis axis : axes) {
                nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue());
            }
        return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, style);
        }
        return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, italic);
    }

    /**
@@ -120,11 +128,18 @@ public class FontFamily {
     * @return
     */
    public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
            boolean isAsset, int weight, boolean isItalic) {
            boolean isAsset, int ttcIndex, int weight, int isItalic,
            FontConfig.Axis[] axes) {
        if (mBuilderPtr == 0) {
            throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
        }
        return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, weight, isItalic);
        if (axes != null) {
            for (FontConfig.Axis axis : axes) {
                nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue());
            }
        }
        return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
                isItalic);
    }

    private static native long nInitBuilder(String lang, int variant);
@@ -137,11 +152,14 @@ public class FontFamily {

    @CriticalNative
    private static native void nUnrefFamily(long nativePtr);
    private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex);
    // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
    // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
    private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
            int weight, int isItalic);
    private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
            int ttcIndex, int weight, boolean isItalic);
            int ttcIndex, int weight, int isItalic);
    private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
            String path, int cookie, boolean isAsset, int weight, boolean isItalic);
            String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);

    // The added axis values are only valid for the next nAddFont* method call.
    @CriticalNative
+15 −6
Original line number Diff line number Diff line
@@ -98,15 +98,17 @@ public class FontListParser {
            } catch (NumberFormatException e) {
                continue;  // ignoreing invalid number format
            }
            int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2),
                    tagString.charAt(3));
            int tag = makeTag(tagString);
            axisList.add(new FontConfig.Axis(tag, styleValue));
        }
        return axisList;
    }

    @VisibleForTesting
    public static int makeTag(char c1, char c2, char c3, char c4) {
    public static int makeTag(String tagString) {
        char c1 = tagString.charAt(0);
        char c2 = tagString.charAt(1);
        char c3 = tagString.charAt(2);
        char c4 = tagString.charAt(3);
        return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
    }

@@ -198,6 +200,13 @@ public class FontListParser {
     */
    private static final Pattern TAG_PATTERN = Pattern.compile("[\\x20-\\x7E]{4}");

    public static boolean isValidTag(String tagString) {
        if (tagString == null || tagString.length() != 4) {
            return false;
        }
        return TAG_PATTERN.matcher(tagString).matches();
    }

    /** The 'styleValue' attribute has an optional leading '-', followed by '<digits>',
     *  '<digits>.<digits>', or '.<digits>' where '<digits>' is one or more of [0-9].
     */
@@ -208,8 +217,8 @@ public class FontListParser {
            throws XmlPullParserException, IOException {
        int tag = 0;
        String tagStr = parser.getAttributeValue(null, "tag");
        if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
            tag = makeTag(tagStr.charAt(0), tagStr.charAt(1), tagStr.charAt(2), tagStr.charAt(3));
        if (isValidTag(tagStr)) {
            tag = makeTag(tagStr);
        } else {
            throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
        }
Loading