FontFamily.cpp revision fb95699364e555148b437cfa1e5c69384f843845
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "Minikin"
18
19#include "JNIHelp.h"
20#include <core_jni_helpers.h>
21
22#include "SkData.h"
23#include "SkFontMgr.h"
24#include "SkRefCnt.h"
25#include "SkTypeface.h"
26#include "GraphicsJNI.h"
27#include <ScopedPrimitiveArray.h>
28#include <ScopedUtfChars.h>
29#include <android_runtime/AndroidRuntime.h>
30#include <android_runtime/android_util_AssetManager.h>
31#include <androidfw/AssetManager.h>
32#include "Utils.h"
33
34#include "TypefaceImpl.h"
35#include <minikin/FontFamily.h>
36#include "MinikinSkia.h"
37
38#include <memory>
39
40namespace android {
41
42static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
43    if (lang == NULL) {
44        return (jlong)new FontFamily(variant);
45    }
46    ScopedUtfChars str(env, lang);
47    uint32_t langId = FontStyle::registerLanguageList(str.c_str());
48    return (jlong)new FontFamily(langId, variant);
49}
50
51static void FontFamily_unref(JNIEnv* env, jobject clazz, jlong familyPtr) {
52    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
53    fontFamily->Unref();
54}
55
56static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
57    MinikinFont* minikinFont = new MinikinFontSkia(face);
58    bool result = family->addFont(minikinFont);
59    minikinFont->Unref();
60    return result;
61}
62
63static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
64        jint ttcIndex) {
65    NPE_CHECK_RETURN_ZERO(env, path);
66    ScopedUtfChars str(env, path);
67    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
68    if (face == NULL) {
69        ALOGE("addFont failed to create font %s", str.c_str());
70        return false;
71    }
72    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
73    return addSkTypeface(fontFamily, face);
74}
75
76static struct {
77    jmethodID mGet;
78    jmethodID mSize;
79} gListClassInfo;
80
81static struct {
82    jfieldID mTag;
83    jfieldID mStyleValue;
84} gAxisClassInfo;
85
86static void release_global_ref(const void* /*data*/, void* context) {
87    JNIEnv* env = AndroidRuntime::getJNIEnv();
88    bool needToAttach = (env == NULL);
89    if (needToAttach) {
90        JavaVMAttachArgs args;
91        args.version = JNI_VERSION_1_4;
92        args.name = "release_font_data";
93        args.group = NULL;
94        jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
95        if (result != JNI_OK) {
96            ALOGE("failed to attach to thread to release global ref.");
97            return;
98        }
99    }
100
101    jobject obj = reinterpret_cast<jobject>(context);
102    env->DeleteGlobalRef(obj);
103
104    if (needToAttach) {
105       AndroidRuntime::getJavaVM()->DetachCurrentThread();
106    }
107}
108
109static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
110        jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
111    NPE_CHECK_RETURN_ZERO(env, font);
112
113    // Declare axis native type.
114    std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes;
115    int skiaAxesLength = 0;
116    if (listOfAxis) {
117        jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize);
118
119        skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]);
120        skiaAxesLength = listSize;
121        for (jint i = 0; i < listSize; ++i) {
122            jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i);
123            if (!axisObject) {
124                skiaAxes[i].fTag = 0;
125                skiaAxes[i].fStyleValue = 0;
126                continue;
127            }
128
129            jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag);
130            jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue);
131            skiaAxes[i].fTag = tag;
132            skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue);
133        }
134    }
135
136    void* fontPtr = env->GetDirectBufferAddress(font);
137    if (fontPtr == NULL) {
138        ALOGE("addFont failed to create font, buffer invalid");
139        return false;
140    }
141    jlong fontSize = env->GetDirectBufferCapacity(font);
142    if (fontSize < 0) {
143        ALOGE("addFont failed to create font, buffer size invalid");
144        return false;
145    }
146    jobject fontRef = MakeGlobalRefOrDie(env, font);
147    SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
148            release_global_ref, reinterpret_cast<void*>(fontRef)));
149    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
150
151    SkFontMgr::FontParameters params;
152    params.setCollectionIndex(ttcIndex);
153    params.setAxes(skiaAxes.get(), skiaAxesLength);
154
155    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
156    SkTypeface* face = fm->createFromStream(fontData.release(), params);
157    if (face == NULL) {
158        ALOGE("addFont failed to create font, invalid request");
159        return false;
160    }
161    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
162    MinikinFont* minikinFont = new MinikinFontSkia(face);
163    fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic));
164    minikinFont->Unref();
165    return true;
166}
167
168static void releaseAsset(const void* ptr, void* context) {
169    delete static_cast<Asset*>(context);
170}
171
172static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPtr,
173        jobject jassetMgr, jstring jpath) {
174    NPE_CHECK_RETURN_ZERO(env, jassetMgr);
175    NPE_CHECK_RETURN_ZERO(env, jpath);
176
177    AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
178    if (NULL == mgr) {
179        return false;
180    }
181
182    ScopedUtfChars str(env, jpath);
183    Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
184    if (NULL == asset) {
185        return false;
186    }
187
188    const void* buf = asset->getBuffer(false);
189    if (NULL == buf) {
190        delete asset;
191        return false;
192    }
193
194    SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset));
195    SkMemoryStream* stream = new SkMemoryStream(data);
196    // CreateFromStream takes ownership of stream.
197    SkTypeface* face = SkTypeface::CreateFromStream(stream);
198    if (face == NULL) {
199        ALOGE("addFontFromAsset failed to create font %s", str.c_str());
200        return false;
201    }
202    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
203    return addSkTypeface(fontFamily, face);
204}
205
206///////////////////////////////////////////////////////////////////////////////
207
208static const JNINativeMethod gFontFamilyMethods[] = {
209    { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
210    { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
211    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
212    { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
213            (void*)FontFamily_addFontWeightStyle },
214    { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
215            (void*)FontFamily_addFontFromAsset },
216};
217
218int register_android_graphics_FontFamily(JNIEnv* env)
219{
220    int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
221            NELEM(gFontFamilyMethods));
222
223    jclass listClass = FindClassOrDie(env, "java/util/List");
224    gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
225    gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
226
227    jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis");
228    gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I");
229    gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F");
230
231    return err;
232}
233
234}
235