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 <hwui/MinikinSkia.h> 35 #include <hwui/Typeface.h> 36 #include <minikin/FontFamily.h> 37 38 #include <memory> 39 40 namespace android { 41 42 static 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 51 static void FontFamily_unref(JNIEnv* env, jobject clazz, jlong familyPtr) { 52 FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); 53 fontFamily->Unref(); 54 } 55 56 static jboolean addSkTypeface(FontFamily* family, SkTypeface* face, const void* fontData, 57 size_t fontSize, int ttcIndex) { 58 MinikinFont* minikinFont = new MinikinFontSkia(face, fontData, fontSize, ttcIndex); 59 bool result = family->addFont(minikinFont); 60 minikinFont->Unref(); 61 return result; 62 } 63 64 static void release_global_ref(const void* /*data*/, void* context) { 65 JNIEnv* env = AndroidRuntime::getJNIEnv(); 66 bool needToAttach = (env == NULL); 67 if (needToAttach) { 68 JavaVMAttachArgs args; 69 args.version = JNI_VERSION_1_4; 70 args.name = "release_font_data"; 71 args.group = NULL; 72 jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); 73 if (result != JNI_OK) { 74 ALOGE("failed to attach to thread to release global ref."); 75 return; 76 } 77 } 78 79 jobject obj = reinterpret_cast<jobject>(context); 80 env->DeleteGlobalRef(obj); 81 82 if (needToAttach) { 83 AndroidRuntime::getJavaVM()->DetachCurrentThread(); 84 } 85 } 86 87 static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jobject bytebuf, 88 jint ttcIndex) { 89 NPE_CHECK_RETURN_ZERO(env, bytebuf); 90 const void* fontPtr = env->GetDirectBufferAddress(bytebuf); 91 if (fontPtr == NULL) { 92 ALOGE("addFont failed to create font, buffer invalid"); 93 return false; 94 } 95 jlong fontSize = env->GetDirectBufferCapacity(bytebuf); 96 if (fontSize < 0) { 97 ALOGE("addFont failed to create font, buffer size invalid"); 98 return false; 99 } 100 jobject fontRef = MakeGlobalRefOrDie(env, bytebuf); 101 SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize, 102 release_global_ref, reinterpret_cast<void*>(fontRef))); 103 std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data)); 104 105 SkFontMgr::FontParameters params; 106 params.setCollectionIndex(ttcIndex); 107 108 SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); 109 SkTypeface* face = fm->createFromStream(fontData.release(), params); 110 if (face == NULL) { 111 ALOGE("addFont failed to create font"); 112 return false; 113 } 114 FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); 115 return addSkTypeface(fontFamily, face, fontPtr, (size_t)fontSize, ttcIndex); 116 } 117 118 static struct { 119 jmethodID mGet; 120 jmethodID mSize; 121 } gListClassInfo; 122 123 static struct { 124 jfieldID mTag; 125 jfieldID mStyleValue; 126 } gAxisClassInfo; 127 128 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr, 129 jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) { 130 NPE_CHECK_RETURN_ZERO(env, font); 131 132 // Declare axis native type. 133 std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes; 134 int skiaAxesLength = 0; 135 if (listOfAxis) { 136 jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize); 137 138 skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]); 139 skiaAxesLength = listSize; 140 for (jint i = 0; i < listSize; ++i) { 141 jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i); 142 if (!axisObject) { 143 skiaAxes[i].fTag = 0; 144 skiaAxes[i].fStyleValue = 0; 145 continue; 146 } 147 148 jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag); 149 jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue); 150 skiaAxes[i].fTag = tag; 151 skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue); 152 } 153 } 154 155 const void* fontPtr = env->GetDirectBufferAddress(font); 156 if (fontPtr == NULL) { 157 ALOGE("addFont failed to create font, buffer invalid"); 158 return false; 159 } 160 jlong fontSize = env->GetDirectBufferCapacity(font); 161 if (fontSize < 0) { 162 ALOGE("addFont failed to create font, buffer size invalid"); 163 return false; 164 } 165 jobject fontRef = MakeGlobalRefOrDie(env, font); 166 SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize, 167 release_global_ref, reinterpret_cast<void*>(fontRef))); 168 std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data)); 169 170 SkFontMgr::FontParameters params; 171 params.setCollectionIndex(ttcIndex); 172 params.setAxes(skiaAxes.get(), skiaAxesLength); 173 174 SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); 175 SkTypeface* face = fm->createFromStream(fontData.release(), params); 176 if (face == NULL) { 177 ALOGE("addFont failed to create font, invalid request"); 178 return false; 179 } 180 FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); 181 MinikinFont* minikinFont = new MinikinFontSkia(face, fontPtr, (size_t)fontSize, ttcIndex); 182 fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic)); 183 minikinFont->Unref(); 184 return true; 185 } 186 187 static void releaseAsset(const void* ptr, void* context) { 188 delete static_cast<Asset*>(context); 189 } 190 191 static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPtr, 192 jobject jassetMgr, jstring jpath) { 193 NPE_CHECK_RETURN_ZERO(env, jassetMgr); 194 NPE_CHECK_RETURN_ZERO(env, jpath); 195 196 AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); 197 if (NULL == mgr) { 198 return false; 199 } 200 201 ScopedUtfChars str(env, jpath); 202 Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); 203 if (NULL == asset) { 204 return false; 205 } 206 207 const void* buf = asset->getBuffer(false); 208 if (NULL == buf) { 209 delete asset; 210 return false; 211 } 212 213 size_t bufSize = asset->getLength(); 214 SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset)); 215 SkMemoryStream* stream = new SkMemoryStream(data); 216 // CreateFromStream takes ownership of stream. 217 SkTypeface* face = SkTypeface::CreateFromStream(stream); 218 if (face == NULL) { 219 ALOGE("addFontFromAsset failed to create font %s", str.c_str()); 220 return false; 221 } 222 FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); 223 return addSkTypeface(fontFamily, face, buf, bufSize, /* ttcIndex */ 0); 224 } 225 226 /////////////////////////////////////////////////////////////////////////////// 227 228 static const JNINativeMethod gFontFamilyMethods[] = { 229 { "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create }, 230 { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, 231 { "nAddFont", "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont }, 232 { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z", 233 (void*)FontFamily_addFontWeightStyle }, 234 { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z", 235 (void*)FontFamily_addFontFromAsset }, 236 }; 237 238 int register_android_graphics_FontFamily(JNIEnv* env) 239 { 240 int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods, 241 NELEM(gFontFamilyMethods)); 242 243 jclass listClass = FindClassOrDie(env, "java/util/List"); 244 gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); 245 gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I"); 246 247 jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis"); 248 gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I"); 249 gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F"); 250 251 return err; 252 } 253 254 } 255