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