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