|
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/Preferences.h" |
|
12 |
|
13 #include "gfx2DGlue.h" |
|
14 #include "gfxFT2FontList.h" |
|
15 #include "gfxImageSurface.h" |
|
16 #include "mozilla/dom/ContentChild.h" |
|
17 #include "nsXULAppAPI.h" |
|
18 #include "nsIScreen.h" |
|
19 #include "nsIScreenManager.h" |
|
20 #include "nsILocaleService.h" |
|
21 #include "nsServiceManagerUtils.h" |
|
22 #include "gfxPrefs.h" |
|
23 #include "cairo.h" |
|
24 |
|
25 #ifdef MOZ_WIDGET_ANDROID |
|
26 #include "AndroidBridge.h" |
|
27 #endif |
|
28 |
|
29 #ifdef MOZ_WIDGET_GONK |
|
30 #include <cutils/properties.h> |
|
31 #endif |
|
32 |
|
33 #include "ft2build.h" |
|
34 #include FT_FREETYPE_H |
|
35 #include FT_MODULE_H |
|
36 |
|
37 using namespace mozilla; |
|
38 using namespace mozilla::dom; |
|
39 using namespace mozilla::gfx; |
|
40 |
|
41 static FT_Library gPlatformFTLibrary = nullptr; |
|
42 |
|
43 class FreetypeReporter MOZ_FINAL : public nsIMemoryReporter, |
|
44 public CountingAllocatorBase<FreetypeReporter> |
|
45 { |
|
46 public: |
|
47 NS_DECL_ISUPPORTS |
|
48 |
|
49 static void* Malloc(FT_Memory, long size) |
|
50 { |
|
51 return CountingMalloc(size); |
|
52 } |
|
53 |
|
54 static void Free(FT_Memory, void* p) |
|
55 { |
|
56 return CountingFree(p); |
|
57 } |
|
58 |
|
59 static void* |
|
60 Realloc(FT_Memory, long cur_size, long new_size, void* p) |
|
61 { |
|
62 return CountingRealloc(p, new_size); |
|
63 } |
|
64 |
|
65 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, |
|
66 nsISupports* aData) |
|
67 { |
|
68 return MOZ_COLLECT_REPORT( |
|
69 "explicit/freetype", KIND_HEAP, UNITS_BYTES, MemoryAllocated(), |
|
70 "Memory used by Freetype."); |
|
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 nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); |
|
96 nsCOMPtr<nsIScreen> screen; |
|
97 screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); |
|
98 mScreenDepth = 24; |
|
99 screen->GetColorDepth(&mScreenDepth); |
|
100 |
|
101 mOffscreenFormat = mScreenDepth == 16 |
|
102 ? gfxImageFormat::RGB16_565 |
|
103 : gfxImageFormat::RGB24; |
|
104 |
|
105 if (gfxPrefs::AndroidRGB16Force()) { |
|
106 mOffscreenFormat = gfxImageFormat::RGB16_565; |
|
107 } |
|
108 |
|
109 #ifdef MOZ_WIDGET_GONK |
|
110 char propQemu[PROPERTY_VALUE_MAX]; |
|
111 property_get("ro.kernel.qemu", propQemu, ""); |
|
112 mIsInGonkEmulator = !strncmp(propQemu, "1", 1); |
|
113 #endif |
|
114 } |
|
115 |
|
116 gfxAndroidPlatform::~gfxAndroidPlatform() |
|
117 { |
|
118 FT_Done_Library(gPlatformFTLibrary); |
|
119 gPlatformFTLibrary = nullptr; |
|
120 } |
|
121 |
|
122 already_AddRefed<gfxASurface> |
|
123 gfxAndroidPlatform::CreateOffscreenSurface(const IntSize& size, |
|
124 gfxContentType contentType) |
|
125 { |
|
126 nsRefPtr<gfxASurface> newSurface; |
|
127 newSurface = new gfxImageSurface(ThebesIntSize(size), |
|
128 OptimalFormatForContent(contentType)); |
|
129 |
|
130 return newSurface.forget(); |
|
131 } |
|
132 |
|
133 static bool |
|
134 IsJapaneseLocale() |
|
135 { |
|
136 static bool sInitialized = false; |
|
137 static bool sIsJapanese = false; |
|
138 |
|
139 if (!sInitialized) { |
|
140 sInitialized = true; |
|
141 |
|
142 do { // to allow 'break' to abandon this block if a call fails |
|
143 nsresult rv; |
|
144 nsCOMPtr<nsILocaleService> ls = |
|
145 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); |
|
146 if (NS_FAILED(rv)) { |
|
147 break; |
|
148 } |
|
149 nsCOMPtr<nsILocale> appLocale; |
|
150 rv = ls->GetApplicationLocale(getter_AddRefs(appLocale)); |
|
151 if (NS_FAILED(rv)) { |
|
152 break; |
|
153 } |
|
154 nsString localeStr; |
|
155 rv = appLocale-> |
|
156 GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr); |
|
157 if (NS_FAILED(rv)) { |
|
158 break; |
|
159 } |
|
160 const nsAString& lang = nsDependentSubstring(localeStr, 0, 2); |
|
161 if (lang.EqualsLiteral("ja")) { |
|
162 sIsJapanese = true; |
|
163 } |
|
164 } while (false); |
|
165 } |
|
166 |
|
167 return sIsJapanese; |
|
168 } |
|
169 |
|
170 void |
|
171 gfxAndroidPlatform::GetCommonFallbackFonts(const uint32_t aCh, |
|
172 int32_t aRunScript, |
|
173 nsTArray<const char*>& aFontList) |
|
174 { |
|
175 static const char kDroidSansJapanese[] = "Droid Sans Japanese"; |
|
176 static const char kMotoyaLMaru[] = "MotoyaLMaru"; |
|
177 |
|
178 if (IS_IN_BMP(aCh)) { |
|
179 // try language-specific "Droid Sans *" fonts for certain blocks, |
|
180 // as most devices probably have these |
|
181 uint8_t block = (aCh >> 8) & 0xff; |
|
182 switch (block) { |
|
183 case 0x05: |
|
184 aFontList.AppendElement("Droid Sans Hebrew"); |
|
185 aFontList.AppendElement("Droid Sans Armenian"); |
|
186 break; |
|
187 case 0x06: |
|
188 aFontList.AppendElement("Droid Sans Arabic"); |
|
189 break; |
|
190 case 0x09: |
|
191 aFontList.AppendElement("Droid Sans Devanagari"); |
|
192 break; |
|
193 case 0x0b: |
|
194 aFontList.AppendElement("Droid Sans Tamil"); |
|
195 break; |
|
196 case 0x0e: |
|
197 aFontList.AppendElement("Droid Sans Thai"); |
|
198 break; |
|
199 case 0x10: case 0x2d: |
|
200 aFontList.AppendElement("Droid Sans Georgian"); |
|
201 break; |
|
202 case 0x12: case 0x13: |
|
203 aFontList.AppendElement("Droid Sans Ethiopic"); |
|
204 break; |
|
205 case 0xf9: case 0xfa: |
|
206 if (IsJapaneseLocale()) { |
|
207 aFontList.AppendElement(kMotoyaLMaru); |
|
208 aFontList.AppendElement(kDroidSansJapanese); |
|
209 } |
|
210 break; |
|
211 default: |
|
212 if (block >= 0x2e && block <= 0x9f && IsJapaneseLocale()) { |
|
213 aFontList.AppendElement(kMotoyaLMaru); |
|
214 aFontList.AppendElement(kDroidSansJapanese); |
|
215 } |
|
216 break; |
|
217 } |
|
218 } |
|
219 // and try Droid Sans Fallback as a last resort |
|
220 aFontList.AppendElement("Droid Sans Fallback"); |
|
221 } |
|
222 |
|
223 nsresult |
|
224 gfxAndroidPlatform::GetFontList(nsIAtom *aLangGroup, |
|
225 const nsACString& aGenericFamily, |
|
226 nsTArray<nsString>& aListOfFonts) |
|
227 { |
|
228 gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup, |
|
229 aGenericFamily, |
|
230 aListOfFonts); |
|
231 return NS_OK; |
|
232 } |
|
233 |
|
234 void |
|
235 gfxAndroidPlatform::GetFontList(InfallibleTArray<FontListEntry>* retValue) |
|
236 { |
|
237 gfxFT2FontList::PlatformFontList()->GetFontList(retValue); |
|
238 } |
|
239 |
|
240 nsresult |
|
241 gfxAndroidPlatform::UpdateFontList() |
|
242 { |
|
243 gfxPlatformFontList::PlatformFontList()->UpdateFontList(); |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 nsresult |
|
248 gfxAndroidPlatform::ResolveFontName(const nsAString& aFontName, |
|
249 FontResolverCallback aCallback, |
|
250 void *aClosure, |
|
251 bool& aAborted) |
|
252 { |
|
253 nsAutoString resolvedName; |
|
254 if (!gfxPlatformFontList::PlatformFontList()-> |
|
255 ResolveFontName(aFontName, resolvedName)) { |
|
256 aAborted = false; |
|
257 return NS_OK; |
|
258 } |
|
259 aAborted = !(*aCallback)(resolvedName, aClosure); |
|
260 return NS_OK; |
|
261 } |
|
262 |
|
263 nsresult |
|
264 gfxAndroidPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) |
|
265 { |
|
266 gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName, aFamilyName); |
|
267 return NS_OK; |
|
268 } |
|
269 |
|
270 gfxPlatformFontList* |
|
271 gfxAndroidPlatform::CreatePlatformFontList() |
|
272 { |
|
273 gfxPlatformFontList* list = new gfxFT2FontList(); |
|
274 if (NS_SUCCEEDED(list->InitFontList())) { |
|
275 return list; |
|
276 } |
|
277 gfxPlatformFontList::Shutdown(); |
|
278 return nullptr; |
|
279 } |
|
280 |
|
281 bool |
|
282 gfxAndroidPlatform::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) |
|
283 { |
|
284 // check for strange format flags |
|
285 NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED), |
|
286 "strange font format hint set"); |
|
287 |
|
288 // accept supported formats |
|
289 if (aFormatFlags & (gfxUserFontSet::FLAG_FORMAT_OPENTYPE | |
|
290 gfxUserFontSet::FLAG_FORMAT_WOFF | |
|
291 gfxUserFontSet::FLAG_FORMAT_TRUETYPE)) { |
|
292 return true; |
|
293 } |
|
294 |
|
295 // reject all other formats, known and unknown |
|
296 if (aFormatFlags != 0) { |
|
297 return false; |
|
298 } |
|
299 |
|
300 // no format hint set, need to look at data |
|
301 return true; |
|
302 } |
|
303 |
|
304 gfxFontGroup * |
|
305 gfxAndroidPlatform::CreateFontGroup(const nsAString &aFamilies, |
|
306 const gfxFontStyle *aStyle, |
|
307 gfxUserFontSet* aUserFontSet) |
|
308 { |
|
309 return new gfxFontGroup(aFamilies, aStyle, aUserFontSet); |
|
310 } |
|
311 |
|
312 FT_Library |
|
313 gfxAndroidPlatform::GetFTLibrary() |
|
314 { |
|
315 return gPlatformFTLibrary; |
|
316 } |
|
317 |
|
318 gfxFontEntry* |
|
319 gfxAndroidPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, |
|
320 const uint8_t *aFontData, uint32_t aLength) |
|
321 { |
|
322 return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, |
|
323 aFontData, |
|
324 aLength); |
|
325 } |
|
326 |
|
327 gfxFontEntry* |
|
328 gfxAndroidPlatform::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, |
|
329 const nsAString& aFontName) |
|
330 { |
|
331 return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aProxyEntry, |
|
332 aFontName); |
|
333 } |
|
334 |
|
335 TemporaryRef<ScaledFont> |
|
336 gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont) |
|
337 { |
|
338 return GetScaledFontForFontWithCairoSkia(aTarget, aFont); |
|
339 } |
|
340 |
|
341 bool |
|
342 gfxAndroidPlatform::FontHintingEnabled() |
|
343 { |
|
344 // In "mobile" builds, we sometimes use non-reflow-zoom, so we |
|
345 // might not want hinting. Let's see. |
|
346 |
|
347 #ifdef MOZ_USING_ANDROID_JAVA_WIDGETS |
|
348 // On android-java, we currently only use gecko to render web |
|
349 // content that can always be be non-reflow-zoomed. So turn off |
|
350 // hinting. |
|
351 // |
|
352 // XXX when gecko-android-java is used as an "app runtime", we may |
|
353 // want to re-enable hinting for non-browser processes there. |
|
354 return false; |
|
355 #endif // MOZ_USING_ANDROID_JAVA_WIDGETS |
|
356 |
|
357 #ifdef MOZ_WIDGET_GONK |
|
358 // On B2G, the UX preference is currently to keep hinting disabled |
|
359 // for all text (see bug 829523). |
|
360 return false; |
|
361 #endif |
|
362 |
|
363 // Currently, we don't have any other targets, but if/when we do, |
|
364 // decide how to handle them here. |
|
365 |
|
366 NS_NOTREACHED("oops, what platform is this?"); |
|
367 return gfxPlatform::FontHintingEnabled(); |
|
368 } |
|
369 |
|
370 bool |
|
371 gfxAndroidPlatform::RequiresLinearZoom() |
|
372 { |
|
373 #ifdef MOZ_USING_ANDROID_JAVA_WIDGETS |
|
374 // On android-java, we currently only use gecko to render web |
|
375 // content that can always be be non-reflow-zoomed. |
|
376 // |
|
377 // XXX when gecko-android-java is used as an "app runtime", we may |
|
378 // want to treat it like B2G and use linear zoom only for the web |
|
379 // browser process, not other apps. |
|
380 return true; |
|
381 #endif |
|
382 |
|
383 #ifdef MOZ_WIDGET_GONK |
|
384 // On B2G, we need linear zoom for the browser, but otherwise prefer |
|
385 // the improved glyph spacing that results from respecting the device |
|
386 // pixel resolution for glyph layout (see bug 816614). |
|
387 return XRE_GetProcessType() == GeckoProcessType_Content && |
|
388 ContentChild::GetSingleton()->IsForBrowser(); |
|
389 #endif |
|
390 |
|
391 NS_NOTREACHED("oops, what platform is this?"); |
|
392 return gfxPlatform::RequiresLinearZoom(); |
|
393 } |
|
394 |
|
395 int |
|
396 gfxAndroidPlatform::GetScreenDepth() const |
|
397 { |
|
398 return mScreenDepth; |
|
399 } |
|
400 |
|
401 bool |
|
402 gfxAndroidPlatform::UseAcceleratedSkiaCanvas() |
|
403 { |
|
404 #ifdef MOZ_WIDGET_ANDROID |
|
405 if (AndroidBridge::Bridge()->GetAPIVersion() < 11) { |
|
406 // It's slower than software due to not having a compositing fast path |
|
407 return false; |
|
408 } |
|
409 #endif |
|
410 |
|
411 return gfxPlatform::UseAcceleratedSkiaCanvas(); |
|
412 } |