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 <nativehelper/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 <nativehelper/ScopedPrimitiveArray.h>
28 #include <nativehelper/ScopedUtfChars.h>
29 #include <android_runtime/AndroidRuntime.h>
30 #include <android_runtime/android_util_AssetManager.h>
31 #include <androidfw/AssetManager2.h>
32 #include "Utils.h"
33 #include "FontUtils.h"
34 
35 #include <hwui/MinikinSkia.h>
36 #include <hwui/Typeface.h>
37 #include <utils/FatVector.h>
38 #include <minikin/FontFamily.h>
39 #include <minikin/LocaleList.h>
40 
41 #include <memory>
42 
43 namespace android {
44 
45 struct NativeFamilyBuilder {
NativeFamilyBuilderandroid::NativeFamilyBuilder46     NativeFamilyBuilder(uint32_t langId, int variant)
47         : langId(langId), variant(static_cast<minikin::FamilyVariant>(variant)) {}
48     uint32_t langId;
49     minikin::FamilyVariant variant;
50     std::vector<minikin::Font> fonts;
51     std::vector<minikin::FontVariation> axes;
52 };
53 
toNativeBuilder(jlong ptr)54 static inline NativeFamilyBuilder* toNativeBuilder(jlong ptr) {
55     return reinterpret_cast<NativeFamilyBuilder*>(ptr);
56 }
57 
toFamily(jlong ptr)58 static inline FontFamilyWrapper* toFamily(jlong ptr) {
59     return reinterpret_cast<FontFamilyWrapper*>(ptr);
60 }
61 
toJLong(Ptr ptr)62 template<typename Ptr> static inline jlong toJLong(Ptr ptr) {
63     return reinterpret_cast<jlong>(ptr);
64 }
65 
FontFamily_initBuilder(JNIEnv * env,jobject clazz,jstring langs,jint variant)66 static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, jint variant) {
67     NativeFamilyBuilder* builder;
68     if (langs != nullptr) {
69         ScopedUtfChars str(env, langs);
70         builder = new NativeFamilyBuilder(minikin::registerLocaleList(str.c_str()), variant);
71     } else {
72         builder = new NativeFamilyBuilder(minikin::registerLocaleList(""), variant);
73     }
74     return toJLong(builder);
75 }
76 
FontFamily_create(jlong builderPtr)77 static jlong FontFamily_create(jlong builderPtr) {
78     if (builderPtr == 0) {
79         return 0;
80     }
81     NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
82     if (builder->fonts.empty()) {
83         return 0;
84     }
85     std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
86             builder->langId, builder->variant, std::move(builder->fonts),
87             true /* isCustomFallback */);
88     if (family->getCoverage().length() == 0) {
89         return 0;
90     }
91     return toJLong(new FontFamilyWrapper(std::move(family)));
92 }
93 
releaseBuilder(jlong builderPtr)94 static void releaseBuilder(jlong builderPtr) {
95     delete toNativeBuilder(builderPtr);
96 }
97 
FontFamily_getBuilderReleaseFunc()98 static jlong FontFamily_getBuilderReleaseFunc() {
99     return toJLong(&releaseBuilder);
100 }
101 
releaseFamily(jlong familyPtr)102 static void releaseFamily(jlong familyPtr) {
103     delete toFamily(familyPtr);
104 }
105 
FontFamily_getFamilyReleaseFunc()106 static jlong FontFamily_getFamilyReleaseFunc() {
107     return toJLong(&releaseFamily);
108 }
109 
addSkTypeface(NativeFamilyBuilder * builder,sk_sp<SkData> && data,int ttcIndex,jint weight,jint italic)110 static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
111         jint weight, jint italic) {
112     uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
113     for (const auto& axis : builder->axes) {
114         skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
115     }
116 
117     const size_t fontSize = data->size();
118     const void* fontPtr = data->data();
119     std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
120 
121     SkFontArguments params;
122     params.setCollectionIndex(ttcIndex);
123     params.setAxes(skiaAxes.data(), skiaAxes.size());
124 
125     sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
126     sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
127     if (face == NULL) {
128         ALOGE("addFont failed to create font, invalid request");
129         builder->axes.clear();
130         return false;
131     }
132     std::shared_ptr<minikin::MinikinFont> minikinFont =
133             std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, "", ttcIndex,
134                     builder->axes);
135     minikin::Font::Builder fontBuilder(minikinFont);
136 
137     if (weight != RESOLVE_BY_FONT_TABLE) {
138         fontBuilder.setWeight(weight);
139     }
140     if (italic != RESOLVE_BY_FONT_TABLE) {
141         fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0));
142     }
143     builder->fonts.push_back(fontBuilder.build());
144     builder->axes.clear();
145     return true;
146 }
147 
release_global_ref(const void *,void * context)148 static void release_global_ref(const void* /*data*/, void* context) {
149     JNIEnv* env = AndroidRuntime::getJNIEnv();
150     bool needToAttach = (env == NULL);
151     if (needToAttach) {
152         JavaVMAttachArgs args;
153         args.version = JNI_VERSION_1_4;
154         args.name = "release_font_data";
155         args.group = NULL;
156         jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
157         if (result != JNI_OK) {
158             ALOGE("failed to attach to thread to release global ref.");
159             return;
160         }
161     }
162 
163     jobject obj = reinterpret_cast<jobject>(context);
164     env->DeleteGlobalRef(obj);
165 
166     if (needToAttach) {
167        AndroidRuntime::getJavaVM()->DetachCurrentThread();
168     }
169 }
170 
FontFamily_addFont(JNIEnv * env,jobject clazz,jlong builderPtr,jobject bytebuf,jint ttcIndex,jint weight,jint isItalic)171 static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf,
172         jint ttcIndex, jint weight, jint isItalic) {
173     NPE_CHECK_RETURN_ZERO(env, bytebuf);
174     NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
175     const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
176     if (fontPtr == NULL) {
177         ALOGE("addFont failed to create font, buffer invalid");
178         builder->axes.clear();
179         return false;
180     }
181     jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
182     if (fontSize < 0) {
183         ALOGE("addFont failed to create font, buffer size invalid");
184         builder->axes.clear();
185         return false;
186     }
187     jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
188     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
189             release_global_ref, reinterpret_cast<void*>(fontRef)));
190     return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
191 }
192 
FontFamily_addFontWeightStyle(JNIEnv * env,jobject clazz,jlong builderPtr,jobject font,jint ttcIndex,jint weight,jint isItalic)193 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr,
194         jobject font, jint ttcIndex, jint weight, jint isItalic) {
195     NPE_CHECK_RETURN_ZERO(env, font);
196     NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
197     const void* fontPtr = env->GetDirectBufferAddress(font);
198     if (fontPtr == NULL) {
199         ALOGE("addFont failed to create font, buffer invalid");
200         builder->axes.clear();
201         return false;
202     }
203     jlong fontSize = env->GetDirectBufferCapacity(font);
204     if (fontSize < 0) {
205         ALOGE("addFont failed to create font, buffer size invalid");
206         builder->axes.clear();
207         return false;
208     }
209     jobject fontRef = MakeGlobalRefOrDie(env, font);
210     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
211             release_global_ref, reinterpret_cast<void*>(fontRef)));
212     return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
213 }
214 
releaseAsset(const void * ptr,void * context)215 static void releaseAsset(const void* ptr, void* context) {
216     delete static_cast<Asset*>(context);
217 }
218 
FontFamily_addFontFromAssetManager(JNIEnv * env,jobject,jlong builderPtr,jobject jassetMgr,jstring jpath,jint cookie,jboolean isAsset,jint ttcIndex,jint weight,jint isItalic)219 static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr,
220         jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex,
221         jint weight, jint isItalic) {
222     NPE_CHECK_RETURN_ZERO(env, jassetMgr);
223     NPE_CHECK_RETURN_ZERO(env, jpath);
224 
225     NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
226     Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
227     if (NULL == mgr) {
228         builder->axes.clear();
229         return false;
230     }
231 
232     ScopedUtfChars str(env, jpath);
233     if (str.c_str() == nullptr) {
234         builder->axes.clear();
235         return false;
236     }
237 
238     std::unique_ptr<Asset> asset;
239     {
240       ScopedLock<AssetManager2> locked_mgr(*mgr);
241       if (isAsset) {
242           asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
243       } else if (cookie > 0) {
244           // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
245           asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
246                   Asset::ACCESS_BUFFER);
247       } else {
248           asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
249       }
250     }
251 
252     if (nullptr == asset) {
253         builder->axes.clear();
254         return false;
255     }
256 
257     const void* buf = asset->getBuffer(false);
258     if (NULL == buf) {
259         builder->axes.clear();
260         return false;
261     }
262 
263     sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
264             asset.release()));
265     return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
266 }
267 
FontFamily_addAxisValue(jlong builderPtr,jint tag,jfloat value)268 static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) {
269     NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
270     builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value});
271 }
272 
273 ///////////////////////////////////////////////////////////////////////////////
274 
275 static const JNINativeMethod gFontFamilyMethods[] = {
276     { "nInitBuilder",           "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
277     { "nCreateFamily",          "(J)J", (void*)FontFamily_create },
278     { "nGetBuilderReleaseFunc", "()J", (void*)FontFamily_getBuilderReleaseFunc },
279     { "nGetFamilyReleaseFunc",  "()J", (void*)FontFamily_getFamilyReleaseFunc },
280     { "nAddFont",               "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
281     { "nAddFontWeightStyle",    "(JLjava/nio/ByteBuffer;III)Z",
282             (void*)FontFamily_addFontWeightStyle },
283     { "nAddFontFromAssetManager",    "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z",
284             (void*)FontFamily_addFontFromAssetManager },
285     { "nAddAxisValue",         "(JIF)V", (void*)FontFamily_addAxisValue },
286 };
287 
register_android_graphics_FontFamily(JNIEnv * env)288 int register_android_graphics_FontFamily(JNIEnv* env)
289 {
290     int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
291             NELEM(gFontFamilyMethods));
292 
293     init_FontUtils(env);
294     return err;
295 }
296 
297 }
298