gfxAndroidPlatform.cpp (14259B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "base/basictypes.h" 8 9 #include "gfxAndroidPlatform.h" 10 #include "mozilla/gfx/2D.h" 11 #include "mozilla/gfx/gfxVars.h" 12 #include "mozilla/CountingAllocatorBase.h" 13 #include "mozilla/intl/LocaleService.h" 14 #include "mozilla/intl/OSPreferences.h" 15 #include "mozilla/java/GeckoAppShellWrappers.h" 16 #include "mozilla/java/HardwareCodecCapabilityUtilsWrappers.h" 17 #include "mozilla/jni/Utils.h" 18 #include "mozilla/layers/AndroidHardwareBuffer.h" 19 #include "mozilla/Preferences.h" 20 #include "mozilla/StaticPrefs_gfx.h" 21 #include "mozilla/StaticPrefs_webgl.h" 22 #include "mozilla/widget/AndroidVsync.h" 23 24 #include "AndroidBuild.h" 25 #include "AndroidSystemFontIterator.h" 26 #include "GeckoProfiler.h" 27 #include "gfx2DGlue.h" 28 #include "gfxFT2FontList.h" 29 #include "gfxImageSurface.h" 30 #include "gfxTextRun.h" 31 #include "nsXULAppAPI.h" 32 #include "nsIScreen.h" 33 #include "nsServiceManagerUtils.h" 34 #include "nsUnicodeProperties.h" 35 #include "cairo.h" 36 #include "VsyncSource.h" 37 38 #include "ft2build.h" 39 #include FT_FREETYPE_H 40 #include FT_MODULE_H 41 42 using namespace mozilla; 43 using namespace mozilla::dom; 44 using namespace mozilla::gfx; 45 using namespace mozilla::unicode; 46 using mozilla::intl::LocaleService; 47 using mozilla::intl::OSPreferences; 48 49 static FT_Library gPlatformFTLibrary = nullptr; 50 51 class FreetypeReporter final : public nsIMemoryReporter, 52 public CountingAllocatorBase<FreetypeReporter> { 53 private: 54 ~FreetypeReporter() {} 55 56 public: 57 NS_DECL_ISUPPORTS 58 59 static void* Malloc(FT_Memory, long size) { return CountingMalloc(size); } 60 61 static void Free(FT_Memory, void* p) { return CountingFree(p); } 62 63 static void* Realloc(FT_Memory, long cur_size, long new_size, void* p) { 64 return CountingRealloc(p, new_size); 65 } 66 67 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, 68 nsISupports* aData, bool aAnonymize) override { 69 MOZ_COLLECT_REPORT("explicit/freetype", KIND_HEAP, UNITS_BYTES, 70 MemoryAllocated(), "Memory used by Freetype."); 71 72 return NS_OK; 73 } 74 }; 75 76 NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter) 77 78 static FT_MemoryRec_ sFreetypeMemoryRecord; 79 80 PRThread* gfxAndroidPlatform::sFontAPIInitializeThread = nullptr; 81 constinit nsCString gfxAndroidPlatform::sManufacturer; 82 83 // static 84 bool gfxAndroidPlatform::IsFontAPIDisabled(bool aDontCheckPref) { 85 if (!aDontCheckPref && 86 StaticPrefs::gfx_font_list_use_font_match_api_force_enabled_AtStartup()) { 87 return false; 88 } 89 90 // Some manufacturer devices seem to crash when using font match API 91 // (Bug 1787551 / Bug 1990734). 92 93 if (sManufacturer.IsEmpty()) { 94 sManufacturer = java::sdk::Build::MANUFACTURER()->ToCString(); 95 } 96 return (sManufacturer.EqualsLiteral("OPPO") || 97 sManufacturer.EqualsLiteral("realme") || 98 sManufacturer.EqualsLiteral("OnePlus") || 99 sManufacturer.EqualsLiteral("HONOR")); 100 } 101 102 // static 103 void gfxAndroidPlatform::InitializeFontAPI() { 104 if (XRE_GetProcessType() != GeckoProcessType_Default) { 105 return; 106 } 107 108 // Android 12+ doesn't read font configuration XML files directly. 109 // It means that it is slow to call font API at first. 110 if (jni::GetAPIVersion() < 31) { 111 return; 112 } 113 114 // This will be called before XPCOM service isn't started. So don't check 115 // preferences. 116 if (IsFontAPIDisabled(true)) { 117 return; 118 } 119 120 if (__builtin_available(android 29, *)) { 121 auto FontAPIInitializeCallback = [](void* aUnused) { 122 AUTO_PROFILER_REGISTER_THREAD("InitializingFontAPI"); 123 PR_SetCurrentThreadName("InitializingFontAPI"); 124 AndroidSystemFontIterator::Preload(); 125 }; 126 sFontAPIInitializeThread = PR_CreateThread( 127 PR_USER_THREAD, FontAPIInitializeCallback, nullptr, PR_PRIORITY_NORMAL, 128 PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); 129 } 130 } 131 132 // static 133 void gfxAndroidPlatform::WaitForInitializeFontAPI() { 134 if (sFontAPIInitializeThread) { 135 PR_JoinThread(sFontAPIInitializeThread); 136 sFontAPIInitializeThread = nullptr; 137 } 138 } 139 140 // static 141 bool gfxAndroidPlatform::IsHwCodecSupported(media::MediaCodec aCodec, 142 bool aEncoder) { 143 switch (aCodec) { 144 case media::MediaCodec::H264: 145 return java::HardwareCodecCapabilityUtils::HasHWH264(aEncoder); 146 case media::MediaCodec::VP8: 147 return java::HardwareCodecCapabilityUtils::HasHWVP8(aEncoder); 148 case media::MediaCodec::VP9: 149 return java::HardwareCodecCapabilityUtils::HasHWVP9(aEncoder); 150 case media::MediaCodec::AV1: 151 return java::HardwareCodecCapabilityUtils::HasHWAV1(aEncoder); 152 case media::MediaCodec::HEVC: 153 return java::HardwareCodecCapabilityUtils::HasHWHEVC(aEncoder); 154 default: 155 return false; 156 } 157 } 158 159 gfxAndroidPlatform::gfxAndroidPlatform() { 160 // A custom allocator. It counts allocations, enabling memory reporting. 161 sFreetypeMemoryRecord.user = nullptr; 162 sFreetypeMemoryRecord.alloc = FreetypeReporter::Malloc; 163 sFreetypeMemoryRecord.free = FreetypeReporter::Free; 164 sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc; 165 166 // These two calls are equivalent to FT_Init_FreeType(), but allow us to 167 // provide a custom memory allocator. 168 FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary); 169 FT_Add_Default_Modules(gPlatformFTLibrary); 170 171 Factory::SetFTLibrary(gPlatformFTLibrary); 172 173 RegisterStrongMemoryReporter(new FreetypeReporter()); 174 175 // Bug 1886573: At this point, we don't yet have primary screen depth. 176 // This setting of screen depth to 0 is preserving existing behavior, 177 // and should be fixed. 178 int32_t screenDepth = 0; 179 mOffscreenFormat = screenDepth == 16 ? SurfaceFormat::R5G6B5_UINT16 180 : SurfaceFormat::X8R8G8B8_UINT32; 181 } 182 183 gfxAndroidPlatform::~gfxAndroidPlatform() { 184 FT_Done_Library(gPlatformFTLibrary); 185 gPlatformFTLibrary = nullptr; 186 layers::AndroidHardwareBufferManager::Shutdown(); 187 layers::AndroidHardwareBufferApi::Shutdown(); 188 } 189 190 void gfxAndroidPlatform::InitAcceleration() { gfxPlatform::InitAcceleration(); } 191 192 already_AddRefed<gfxASurface> gfxAndroidPlatform::CreateOffscreenSurface( 193 const IntSize& aSize, gfxImageFormat aFormat) { 194 if (!Factory::AllowedSurfaceSize(aSize)) { 195 return nullptr; 196 } 197 198 RefPtr<gfxASurface> newSurface; 199 newSurface = new gfxImageSurface(aSize, aFormat); 200 201 return newSurface.forget(); 202 } 203 204 static bool IsJapaneseLocale() { 205 static bool sInitialized = false; 206 static bool sIsJapanese = false; 207 208 if (!sInitialized) { 209 sInitialized = true; 210 211 nsAutoCString appLocale; 212 LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale); 213 214 const nsDependentCSubstring lang(appLocale, 0, 2); 215 if (lang.EqualsLiteral("ja")) { 216 sIsJapanese = true; 217 } else { 218 OSPreferences::GetInstance()->GetSystemLocale(appLocale); 219 220 const nsDependentCSubstring lang(appLocale, 0, 2); 221 if (lang.EqualsLiteral("ja")) { 222 sIsJapanese = true; 223 } 224 } 225 } 226 227 return sIsJapanese; 228 } 229 230 void gfxAndroidPlatform::GetCommonFallbackFonts( 231 uint32_t aCh, Script aRunScript, FontPresentation aPresentation, 232 nsTArray<const char*>& aFontList) { 233 static const char kDroidSansJapanese[] = "Droid Sans Japanese"; 234 static const char kMotoyaLMaru[] = "MotoyaLMaru"; 235 static const char kNotoSansCJKJP[] = "Noto Sans CJK JP"; 236 static const char kNotoColorEmoji[] = "Noto Color Emoji"; 237 238 if (PrefersColor(aPresentation)) { 239 aFontList.AppendElement(kNotoColorEmoji); 240 } 241 242 if (IS_IN_BMP(aCh)) { 243 // try language-specific "Droid Sans *" and "Noto Sans *" fonts for 244 // certain blocks, as most devices probably have these 245 uint8_t block = (aCh >> 8) & 0xff; 246 switch (block) { 247 case 0x05: 248 aFontList.AppendElement("Noto Sans Hebrew"); 249 aFontList.AppendElement("Droid Sans Hebrew"); 250 aFontList.AppendElement("Noto Sans Armenian"); 251 aFontList.AppendElement("Droid Sans Armenian"); 252 break; 253 case 0x06: 254 aFontList.AppendElement("Noto Sans Arabic"); 255 aFontList.AppendElement("Droid Sans Arabic"); 256 break; 257 case 0x09: 258 aFontList.AppendElement("Noto Sans Devanagari"); 259 aFontList.AppendElement("Noto Sans Bengali"); 260 aFontList.AppendElement("Droid Sans Devanagari"); 261 break; 262 case 0x0a: 263 aFontList.AppendElement("Noto Sans Gurmukhi"); 264 aFontList.AppendElement("Noto Sans Gujarati"); 265 break; 266 case 0x0b: 267 aFontList.AppendElement("Noto Sans Tamil"); 268 aFontList.AppendElement("Noto Sans Oriya"); 269 aFontList.AppendElement("Droid Sans Tamil"); 270 break; 271 case 0x0c: 272 aFontList.AppendElement("Noto Sans Telugu"); 273 aFontList.AppendElement("Noto Sans Kannada"); 274 break; 275 case 0x0d: 276 aFontList.AppendElement("Noto Sans Malayalam"); 277 aFontList.AppendElement("Noto Sans Sinhala"); 278 break; 279 case 0x0e: 280 aFontList.AppendElement("Noto Sans Thai"); 281 aFontList.AppendElement("Noto Sans Lao"); 282 aFontList.AppendElement("Droid Sans Thai"); 283 break; 284 case 0x0f: 285 aFontList.AppendElement("Noto Sans Tibetan"); 286 break; 287 case 0x10: 288 case 0x2d: 289 aFontList.AppendElement("Noto Sans Georgian"); 290 aFontList.AppendElement("Droid Sans Georgian"); 291 break; 292 case 0x12: 293 case 0x13: 294 aFontList.AppendElement("Noto Sans Ethiopic"); 295 aFontList.AppendElement("Droid Sans Ethiopic"); 296 break; 297 case 0x21: 298 case 0x23: 299 case 0x24: 300 case 0x26: 301 case 0x27: 302 case 0x29: 303 aFontList.AppendElement("Noto Sans Symbols"); 304 break; 305 case 0xf9: 306 case 0xfa: 307 if (IsJapaneseLocale()) { 308 aFontList.AppendElement(kMotoyaLMaru); 309 aFontList.AppendElement(kNotoSansCJKJP); 310 aFontList.AppendElement(kDroidSansJapanese); 311 } 312 break; 313 default: 314 if (block >= 0x2e && block <= 0x9f && IsJapaneseLocale()) { 315 aFontList.AppendElement(kMotoyaLMaru); 316 aFontList.AppendElement(kNotoSansCJKJP); 317 aFontList.AppendElement(kDroidSansJapanese); 318 } 319 break; 320 } 321 } 322 // and try Droid Sans Fallback as a last resort 323 aFontList.AppendElement("Droid Sans Fallback"); 324 } 325 326 bool gfxAndroidPlatform::CreatePlatformFontList() { 327 return gfxPlatformFontList::Initialize(new gfxFT2FontList); 328 } 329 330 void gfxAndroidPlatform::ReadSystemFontList( 331 mozilla::dom::SystemFontList* aFontList) { 332 gfxFT2FontList::PlatformFontList()->ReadSystemFontList(aFontList); 333 } 334 335 bool gfxAndroidPlatform::FontHintingEnabled() { 336 // In "mobile" builds, we sometimes use non-reflow-zoom, so we 337 // might not want hinting. Let's see. 338 339 #ifdef MOZ_WIDGET_ANDROID 340 // On Android, we currently only use gecko to render web 341 // content that can always be be non-reflow-zoomed. So turn off 342 // hinting. 343 // 344 // XXX when gecko-android-java is used as an "app runtime", we may 345 // want to re-enable hinting for non-browser processes there. 346 // 347 // If this value is changed, we must ensure that the argument passed 348 // to SkInitCairoFT() in GPUParent::RecvInit() is also updated. 349 return false; 350 #endif // MOZ_WIDGET_ANDROID 351 352 // Currently, we don't have any other targets, but if/when we do, 353 // decide how to handle them here. 354 355 MOZ_ASSERT_UNREACHABLE("oops, what platform is this?"); 356 return gfxPlatform::FontHintingEnabled(); 357 } 358 359 bool gfxAndroidPlatform::RequiresLinearZoom() { 360 #ifdef MOZ_WIDGET_ANDROID 361 // On Android, we currently only use gecko to render web 362 // content that can always be be non-reflow-zoomed. 363 // 364 // XXX when gecko-android-java is used as an "app runtime", we may 365 // want to use linear zoom only for the web browser process, not other apps. 366 return true; 367 #endif 368 369 MOZ_ASSERT_UNREACHABLE("oops, what platform is this?"); 370 return gfxPlatform::RequiresLinearZoom(); 371 } 372 373 class AndroidVsyncSource final : public VsyncSource, 374 public widget::AndroidVsync::Observer { 375 public: 376 AndroidVsyncSource() : mAndroidVsync(widget::AndroidVsync::GetInstance()) {} 377 378 bool IsVsyncEnabled() override { 379 MOZ_ASSERT(NS_IsMainThread()); 380 return mObservingVsync; 381 } 382 383 void EnableVsync() override { 384 MOZ_ASSERT(NS_IsMainThread()); 385 386 if (mObservingVsync) { 387 return; 388 } 389 390 float fps = java::GeckoAppShell::GetScreenRefreshRate(); 391 MOZ_ASSERT(fps > 0.0f); 392 mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / fps); 393 mAndroidVsync->RegisterObserver(this, widget::AndroidVsync::RENDER); 394 mObservingVsync = true; 395 } 396 397 void DisableVsync() override { 398 MOZ_ASSERT(NS_IsMainThread()); 399 400 if (!mObservingVsync) { 401 return; 402 } 403 mAndroidVsync->UnregisterObserver(this, widget::AndroidVsync::RENDER); 404 mObservingVsync = false; 405 } 406 407 TimeDuration GetVsyncRate() override { return mVsyncRate; } 408 409 void Shutdown() override { DisableVsync(); } 410 411 // Override for the widget::AndroidVsync::Observer method 412 void OnVsync(const TimeStamp& aTimeStamp) override { 413 // Use the timebase from the frame callback as the vsync time, unless it 414 // is in the future. 415 TimeStamp now = TimeStamp::Now(); 416 TimeStamp vsyncTime = aTimeStamp < now ? aTimeStamp : now; 417 TimeStamp outputTime = vsyncTime + GetVsyncRate(); 418 419 NotifyVsync(vsyncTime, outputTime); 420 } 421 422 void OnMaybeUpdateRefreshRate() override { 423 NS_DispatchToMainThread( 424 NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() { 425 if (!self->mObservingVsync) { 426 return; 427 } 428 self->DisableVsync(); 429 self->EnableVsync(); 430 })); 431 } 432 433 private: 434 virtual ~AndroidVsyncSource() { DisableVsync(); } 435 436 RefPtr<widget::AndroidVsync> mAndroidVsync; 437 TimeDuration mVsyncRate; 438 bool mObservingVsync = false; 439 }; 440 441 already_AddRefed<mozilla::gfx::VsyncSource> 442 gfxAndroidPlatform::CreateGlobalHardwareVsyncSource() { 443 RefPtr<AndroidVsyncSource> vsyncSource = new AndroidVsyncSource(); 444 return vsyncSource.forget(); 445 }