1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* vim: set ts=8 sts=4 et sw=4 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/CountingAllocatorBase.h" 12 #include "mozilla/Preferences.h" 13 14 #include "gfx2DGlue.h" 15 #include "gfxFT2FontList.h" 16 #include "gfxImageSurface.h" 17 #include "gfxTextRun.h" 18 #include "mozilla/dom/ContentChild.h" 19 #include "nsXULAppAPI.h" 20 #include "nsIScreen.h" 21 #include "nsIScreenManager.h" 22 #include "nsILocaleService.h" 23 #include "nsServiceManagerUtils.h" 24 #include "gfxPrefs.h" 25 #include "cairo.h" 26 #include "VsyncSource.h" 27 28 #include "ft2build.h" 29 #include FT_FREETYPE_H 30 #include FT_MODULE_H 31 32 using namespace mozilla; 33 using namespace mozilla::dom; 34 using namespace mozilla::gfx; 35 36 static FT_Library gPlatformFTLibrary = nullptr; 37 38 class FreetypeReporter final : public nsIMemoryReporter, 39 public CountingAllocatorBase<FreetypeReporter> 40 { 41 private: 42 ~FreetypeReporter() {} 43 44 public: 45 NS_DECL_ISUPPORTS 46 47 static void* Malloc(FT_Memory, long size) 48 { 49 return CountingMalloc(size); 50 } 51 52 static void Free(FT_Memory, void* p) 53 { 54 return CountingFree(p); 55 } 56 57 static void* 58 Realloc(FT_Memory, long cur_size, long new_size, void* p) 59 { 60 return CountingRealloc(p, new_size); 61 } 62 63 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, 64 nsISupports* aData, bool aAnonymize) override 65 { 66 MOZ_COLLECT_REPORT( 67 "explicit/freetype", KIND_HEAP, UNITS_BYTES, MemoryAllocated(), 68 "Memory used by Freetype."); 69 70 return NS_OK; 71 } 72 }; 73 74 NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter) 75 76 template<> Atomic<size_t> CountingAllocatorBase<FreetypeReporter>::sAmount(0); 77 78 static FT_MemoryRec_ sFreetypeMemoryRecord; 79 80 gfxAndroidPlatform::gfxAndroidPlatform() 81 { 82 // A custom allocator. It counts allocations, enabling memory reporting. 83 sFreetypeMemoryRecord.user = nullptr; 84 sFreetypeMemoryRecord.alloc = FreetypeReporter::Malloc; 85 sFreetypeMemoryRecord.free = FreetypeReporter::Free; 86 sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc; 87 88 // These two calls are equivalent to FT_Init_FreeType(), but allow us to 89 // provide a custom memory allocator. 90 FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary); 91 FT_Add_Default_Modules(gPlatformFTLibrary); 92 93 RegisterStrongMemoryReporter(new FreetypeReporter()); 94 95 mOffscreenFormat = GetScreenDepth() == 16 96 ? SurfaceFormat::R5G6B5_UINT16 97 : SurfaceFormat::X8R8G8B8_UINT32; 98 99 if (gfxPrefs::AndroidRGB16Force()) { 100 mOffscreenFormat = SurfaceFormat::R5G6B5_UINT16; 101 } 102 } 103 104 gfxAndroidPlatform::~gfxAndroidPlatform() 105 { 106 FT_Done_Library(gPlatformFTLibrary); 107 gPlatformFTLibrary = nullptr; 108 } 109 110 already_AddRefed<gfxASurface> 111 gfxAndroidPlatform::CreateOffscreenSurface(const IntSize& aSize, 112 gfxImageFormat aFormat) 113 { 114 if (!Factory::AllowedSurfaceSize(aSize)) { 115 return nullptr; 116 } 117 118 RefPtr<gfxASurface> newSurface; 119 newSurface = new gfxImageSurface(aSize, aFormat); 120 121 return newSurface.forget(); 122 } 123 124 static bool 125 IsJapaneseLocale() 126 { 127 static bool sInitialized = false; 128 static bool sIsJapanese = false; 129 130 if (!sInitialized) { 131 sInitialized = true; 132 133 do { // to allow 'break' to abandon this block if a call fails 134 nsresult rv; 135 nsCOMPtr<nsILocaleService> ls = 136 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); 137 if (NS_FAILED(rv)) { 138 break; 139 } 140 nsCOMPtr<nsILocale> appLocale; 141 rv = ls->GetApplicationLocale(getter_AddRefs(appLocale)); 142 if (NS_FAILED(rv)) { 143 break; 144 } 145 nsString localeStr; 146 rv = appLocale-> 147 GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr); 148 if (NS_FAILED(rv)) { 149 break; 150 } 151 const nsAString& lang = nsDependentSubstring(localeStr, 0, 2); 152 if (lang.EqualsLiteral("ja")) { 153 sIsJapanese = true; 154 } 155 } while (false); 156 } 157 158 return sIsJapanese; 159 } 160 161 void 162 gfxAndroidPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, 163 Script aRunScript, 164 nsTArray<const char*>& aFontList) 165 { 166 static const char kDroidSansJapanese[] = "Droid Sans Japanese"; 167 static const char kMotoyaLMaru[] = "MotoyaLMaru"; 168 static const char kNotoSansCJKJP[] = "Noto Sans CJK JP"; 169 static const char kNotoColorEmoji[] = "Noto Color Emoji"; 170 171 if (aNextCh == 0xfe0fu) { 172 // if char is followed by VS16, try for a color emoji glyph 173 aFontList.AppendElement(kNotoColorEmoji); 174 } 175 176 if (!IS_IN_BMP(aCh)) { 177 uint32_t p = aCh >> 16; 178 if (p == 1) { // try color emoji font, unless VS15 (text style) present 179 if (aNextCh != 0xfe0fu && aNextCh != 0xfe0eu) { 180 aFontList.AppendElement(kNotoColorEmoji); 181 } 182 } 183 } else { 184 // try language-specific "Droid Sans *" and "Noto Sans *" fonts for 185 // certain blocks, as most devices probably have these 186 uint8_t block = (aCh >> 8) & 0xff; 187 switch (block) { 188 case 0x05: 189 aFontList.AppendElement("Droid Sans Hebrew"); 190 aFontList.AppendElement("Droid Sans Armenian"); 191 break; 192 case 0x06: 193 aFontList.AppendElement("Droid Sans Arabic"); 194 break; 195 case 0x09: 196 aFontList.AppendElement("Noto Sans Devanagari"); 197 aFontList.AppendElement("Droid Sans Devanagari"); 198 break; 199 case 0x0b: 200 aFontList.AppendElement("Noto Sans Tamil"); 201 aFontList.AppendElement("Droid Sans Tamil"); 202 break; 203 case 0x0e: 204 aFontList.AppendElement("Noto Sans Thai"); 205 aFontList.AppendElement("Droid Sans Thai"); 206 break; 207 case 0x10: case 0x2d: 208 aFontList.AppendElement("Droid Sans Georgian"); 209 break; 210 case 0x12: case 0x13: 211 aFontList.AppendElement("Droid Sans Ethiopic"); 212 break; 213 case 0xf9: case 0xfa: 214 if (IsJapaneseLocale()) { 215 aFontList.AppendElement(kMotoyaLMaru); 216 aFontList.AppendElement(kNotoSansCJKJP); 217 aFontList.AppendElement(kDroidSansJapanese); 218 } 219 break; 220 default: 221 if (block >= 0x2e && block <= 0x9f && IsJapaneseLocale()) { 222 aFontList.AppendElement(kMotoyaLMaru); 223 aFontList.AppendElement(kNotoSansCJKJP); 224 aFontList.AppendElement(kDroidSansJapanese); 225 } 226 break; 227 } 228 } 229 // and try Droid Sans Fallback as a last resort 230 aFontList.AppendElement("Droid Sans Fallback"); 231 } 232 233 void 234 gfxAndroidPlatform::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue) 235 { 236 gfxFT2FontList::PlatformFontList()->GetSystemFontList(retValue); 237 } 238 239 gfxPlatformFontList* 240 gfxAndroidPlatform::CreatePlatformFontList() 241 { 242 gfxPlatformFontList* list = new gfxFT2FontList(); 243 if (NS_SUCCEEDED(list->InitFontList())) { 244 return list; 245 } 246 gfxPlatformFontList::Shutdown(); 247 return nullptr; 248 } 249 250 bool 251 gfxAndroidPlatform::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) 252 { 253 // check for strange format flags 254 NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED), 255 "strange font format hint set"); 256 257 // accept supported formats 258 if (aFormatFlags & gfxUserFontSet::FLAG_FORMATS_COMMON) { 259 return true; 260 } 261 262 // reject all other formats, known and unknown 263 if (aFormatFlags != 0) { 264 return false; 265 } 266 267 // no format hint set, need to look at data 268 return true; 269 } 270 271 gfxFontGroup * 272 gfxAndroidPlatform::CreateFontGroup(const FontFamilyList& aFontFamilyList, 273 const gfxFontStyle* aStyle, 274 gfxTextPerfMetrics* aTextPerf, 275 gfxUserFontSet* aUserFontSet, 276 gfxFloat aDevToCssSize) 277 { 278 return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf, 279 aUserFontSet, aDevToCssSize); 280 } 281 282 FT_Library 283 gfxAndroidPlatform::GetFTLibrary() 284 { 285 return gPlatformFTLibrary; 286 } 287 288 already_AddRefed<ScaledFont> 289 gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont) 290 { 291 return GetScaledFontForFontWithCairoSkia(aTarget, aFont); 292 } 293 294 bool 295 gfxAndroidPlatform::FontHintingEnabled() 296 { 297 // In "mobile" builds, we sometimes use non-reflow-zoom, so we 298 // might not want hinting. Let's see. 299 300 #ifdef MOZ_WIDGET_ANDROID 301 // On Android, we currently only use gecko to render web 302 // content that can always be be non-reflow-zoomed. So turn off 303 // hinting. 304 // 305 // XXX when gecko-android-java is used as an "app runtime", we may 306 // want to re-enable hinting for non-browser processes there. 307 return false; 308 #endif // MOZ_WIDGET_ANDROID 309 310 // Currently, we don't have any other targets, but if/when we do, 311 // decide how to handle them here. 312 313 NS_NOTREACHED("oops, what platform is this?"); 314 return gfxPlatform::FontHintingEnabled(); 315 } 316 317 bool 318 gfxAndroidPlatform::RequiresLinearZoom() 319 { 320 #ifdef MOZ_WIDGET_ANDROID 321 // On Android, we currently only use gecko to render web 322 // content that can always be be non-reflow-zoomed. 323 // 324 // XXX when gecko-android-java is used as an "app runtime", we may 325 // want to treat it like B2G and use linear zoom only for the web 326 // browser process, not other apps. 327 return true; 328 #endif 329 330 NS_NOTREACHED("oops, what platform is this?"); 331 return gfxPlatform::RequiresLinearZoom(); 332 } 333 334 already_AddRefed<mozilla::gfx::VsyncSource> 335 gfxAndroidPlatform::CreateHardwareVsyncSource() 336 { 337 return gfxPlatform::CreateHardwareVsyncSource(); 338 } 339