summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxAndroidPlatform.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxAndroidPlatform.cpp')
-rw-r--r--gfx/thebes/gfxAndroidPlatform.cpp369
1 files changed, 369 insertions, 0 deletions
diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp
new file mode 100644
index 0000000000..7c7fe11fc6
--- /dev/null
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+
+#include "gfxAndroidPlatform.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/CountingAllocatorBase.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/intl/OSPreferences.h"
+#include "mozilla/java/GeckoAppShellWrappers.h"
+#include "mozilla/jni/Utils.h"
+#include "mozilla/layers/AndroidHardwareBuffer.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_webgl.h"
+#include "mozilla/widget/AndroidVsync.h"
+
+#include "gfx2DGlue.h"
+#include "gfxFT2FontList.h"
+#include "gfxImageSurface.h"
+#include "gfxTextRun.h"
+#include "nsXULAppAPI.h"
+#include "nsIScreen.h"
+#include "nsServiceManagerUtils.h"
+#include "nsUnicodeProperties.h"
+#include "cairo.h"
+#include "VsyncSource.h"
+
+#include "ft2build.h"
+#include FT_FREETYPE_H
+#include FT_MODULE_H
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::unicode;
+using mozilla::intl::LocaleService;
+using mozilla::intl::OSPreferences;
+
+static FT_Library gPlatformFTLibrary = nullptr;
+
+class FreetypeReporter final : public nsIMemoryReporter,
+ public CountingAllocatorBase<FreetypeReporter> {
+ private:
+ ~FreetypeReporter() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ static void* Malloc(FT_Memory, long size) { return CountingMalloc(size); }
+
+ static void Free(FT_Memory, void* p) { return CountingFree(p); }
+
+ static void* Realloc(FT_Memory, long cur_size, long new_size, void* p) {
+ return CountingRealloc(p, new_size);
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override {
+ MOZ_COLLECT_REPORT("explicit/freetype", KIND_HEAP, UNITS_BYTES,
+ MemoryAllocated(), "Memory used by Freetype.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter)
+
+static FT_MemoryRec_ sFreetypeMemoryRecord;
+
+gfxAndroidPlatform::gfxAndroidPlatform() {
+ // A custom allocator. It counts allocations, enabling memory reporting.
+ sFreetypeMemoryRecord.user = nullptr;
+ sFreetypeMemoryRecord.alloc = FreetypeReporter::Malloc;
+ sFreetypeMemoryRecord.free = FreetypeReporter::Free;
+ sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc;
+
+ // These two calls are equivalent to FT_Init_FreeType(), but allow us to
+ // provide a custom memory allocator.
+ FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary);
+ FT_Add_Default_Modules(gPlatformFTLibrary);
+
+ Factory::SetFTLibrary(gPlatformFTLibrary);
+
+ RegisterStrongMemoryReporter(new FreetypeReporter());
+
+ mOffscreenFormat = GetScreenDepth() == 16 ? SurfaceFormat::R5G6B5_UINT16
+ : SurfaceFormat::X8R8G8B8_UINT32;
+
+ if (StaticPrefs::gfx_android_rgb16_force_AtStartup()) {
+ mOffscreenFormat = SurfaceFormat::R5G6B5_UINT16;
+ }
+}
+
+gfxAndroidPlatform::~gfxAndroidPlatform() {
+ FT_Done_Library(gPlatformFTLibrary);
+ gPlatformFTLibrary = nullptr;
+ layers::AndroidHardwareBufferManager::Shutdown();
+ layers::AndroidHardwareBufferApi::Shutdown();
+}
+
+void gfxAndroidPlatform::InitAcceleration() { gfxPlatform::InitAcceleration(); }
+
+already_AddRefed<gfxASurface> gfxAndroidPlatform::CreateOffscreenSurface(
+ const IntSize& aSize, gfxImageFormat aFormat) {
+ if (!Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ RefPtr<gfxASurface> newSurface;
+ newSurface = new gfxImageSurface(aSize, aFormat);
+
+ return newSurface.forget();
+}
+
+static bool IsJapaneseLocale() {
+ static bool sInitialized = false;
+ static bool sIsJapanese = false;
+
+ if (!sInitialized) {
+ sInitialized = true;
+
+ nsAutoCString appLocale;
+ LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
+
+ const nsDependentCSubstring lang(appLocale, 0, 2);
+ if (lang.EqualsLiteral("ja")) {
+ sIsJapanese = true;
+ } else {
+ OSPreferences::GetInstance()->GetSystemLocale(appLocale);
+
+ const nsDependentCSubstring lang(appLocale, 0, 2);
+ if (lang.EqualsLiteral("ja")) {
+ sIsJapanese = true;
+ }
+ }
+ }
+
+ return sIsJapanese;
+}
+
+void gfxAndroidPlatform::GetCommonFallbackFonts(
+ uint32_t aCh, Script aRunScript, eFontPresentation aPresentation,
+ nsTArray<const char*>& aFontList) {
+ static const char kDroidSansJapanese[] = "Droid Sans Japanese";
+ static const char kMotoyaLMaru[] = "MotoyaLMaru";
+ static const char kNotoSansCJKJP[] = "Noto Sans CJK JP";
+ static const char kNotoColorEmoji[] = "Noto Color Emoji";
+
+ if (PrefersColor(aPresentation)) {
+ aFontList.AppendElement(kNotoColorEmoji);
+ }
+
+ if (IS_IN_BMP(aCh)) {
+ // try language-specific "Droid Sans *" and "Noto Sans *" fonts for
+ // certain blocks, as most devices probably have these
+ uint8_t block = (aCh >> 8) & 0xff;
+ switch (block) {
+ case 0x05:
+ aFontList.AppendElement("Noto Sans Hebrew");
+ aFontList.AppendElement("Droid Sans Hebrew");
+ aFontList.AppendElement("Noto Sans Armenian");
+ aFontList.AppendElement("Droid Sans Armenian");
+ break;
+ case 0x06:
+ aFontList.AppendElement("Noto Sans Arabic");
+ aFontList.AppendElement("Droid Sans Arabic");
+ break;
+ case 0x09:
+ aFontList.AppendElement("Noto Sans Devanagari");
+ aFontList.AppendElement("Noto Sans Bengali");
+ aFontList.AppendElement("Droid Sans Devanagari");
+ break;
+ case 0x0a:
+ aFontList.AppendElement("Noto Sans Gurmukhi");
+ aFontList.AppendElement("Noto Sans Gujarati");
+ break;
+ case 0x0b:
+ aFontList.AppendElement("Noto Sans Tamil");
+ aFontList.AppendElement("Noto Sans Oriya");
+ aFontList.AppendElement("Droid Sans Tamil");
+ break;
+ case 0x0c:
+ aFontList.AppendElement("Noto Sans Telugu");
+ aFontList.AppendElement("Noto Sans Kannada");
+ break;
+ case 0x0d:
+ aFontList.AppendElement("Noto Sans Malayalam");
+ aFontList.AppendElement("Noto Sans Sinhala");
+ break;
+ case 0x0e:
+ aFontList.AppendElement("Noto Sans Thai");
+ aFontList.AppendElement("Noto Sans Lao");
+ aFontList.AppendElement("Droid Sans Thai");
+ break;
+ case 0x0f:
+ aFontList.AppendElement("Noto Sans Tibetan");
+ break;
+ case 0x10:
+ case 0x2d:
+ aFontList.AppendElement("Noto Sans Georgian");
+ aFontList.AppendElement("Droid Sans Georgian");
+ break;
+ case 0x12:
+ case 0x13:
+ aFontList.AppendElement("Noto Sans Ethiopic");
+ aFontList.AppendElement("Droid Sans Ethiopic");
+ break;
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x26:
+ case 0x27:
+ case 0x29:
+ aFontList.AppendElement("Noto Sans Symbols");
+ break;
+ case 0xf9:
+ case 0xfa:
+ if (IsJapaneseLocale()) {
+ aFontList.AppendElement(kMotoyaLMaru);
+ aFontList.AppendElement(kNotoSansCJKJP);
+ aFontList.AppendElement(kDroidSansJapanese);
+ }
+ break;
+ default:
+ if (block >= 0x2e && block <= 0x9f && IsJapaneseLocale()) {
+ aFontList.AppendElement(kMotoyaLMaru);
+ aFontList.AppendElement(kNotoSansCJKJP);
+ aFontList.AppendElement(kDroidSansJapanese);
+ }
+ break;
+ }
+ }
+ // and try Droid Sans Fallback as a last resort
+ aFontList.AppendElement("Droid Sans Fallback");
+}
+
+bool gfxAndroidPlatform::CreatePlatformFontList() {
+ return gfxPlatformFontList::Initialize(new gfxFT2FontList);
+}
+
+void gfxAndroidPlatform::ReadSystemFontList(
+ mozilla::dom::SystemFontList* aFontList) {
+ gfxFT2FontList::PlatformFontList()->ReadSystemFontList(aFontList);
+}
+
+bool gfxAndroidPlatform::FontHintingEnabled() {
+ // In "mobile" builds, we sometimes use non-reflow-zoom, so we
+ // might not want hinting. Let's see.
+
+#ifdef MOZ_WIDGET_ANDROID
+ // On Android, we currently only use gecko to render web
+ // content that can always be be non-reflow-zoomed. So turn off
+ // hinting.
+ //
+ // XXX when gecko-android-java is used as an "app runtime", we may
+ // want to re-enable hinting for non-browser processes there.
+ //
+ // If this value is changed, we must ensure that the argument passed
+ // to SkInitCairoFT() in GPUParent::RecvInit() is also updated.
+ return false;
+#endif // MOZ_WIDGET_ANDROID
+
+ // Currently, we don't have any other targets, but if/when we do,
+ // decide how to handle them here.
+
+ MOZ_ASSERT_UNREACHABLE("oops, what platform is this?");
+ return gfxPlatform::FontHintingEnabled();
+}
+
+bool gfxAndroidPlatform::RequiresLinearZoom() {
+#ifdef MOZ_WIDGET_ANDROID
+ // On Android, we currently only use gecko to render web
+ // content that can always be be non-reflow-zoomed.
+ //
+ // XXX when gecko-android-java is used as an "app runtime", we may
+ // want to use linear zoom only for the web browser process, not other apps.
+ return true;
+#endif
+
+ MOZ_ASSERT_UNREACHABLE("oops, what platform is this?");
+ return gfxPlatform::RequiresLinearZoom();
+}
+
+class AndroidVsyncSource final : public VsyncSource,
+ public widget::AndroidVsync::Observer {
+ public:
+ AndroidVsyncSource() : mAndroidVsync(widget::AndroidVsync::GetInstance()) {}
+
+ bool IsVsyncEnabled() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mObservingVsync;
+ }
+
+ void EnableVsync() override {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mObservingVsync) {
+ return;
+ }
+
+ float fps = java::GeckoAppShell::GetScreenRefreshRate();
+ MOZ_ASSERT(fps > 0.0f);
+ mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / fps);
+ mAndroidVsync->RegisterObserver(this, widget::AndroidVsync::RENDER);
+ mObservingVsync = true;
+ }
+
+ void DisableVsync() override {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mObservingVsync) {
+ return;
+ }
+ mAndroidVsync->UnregisterObserver(this, widget::AndroidVsync::RENDER);
+ mObservingVsync = false;
+ }
+
+ TimeDuration GetVsyncRate() override { return mVsyncRate; }
+
+ void Shutdown() override { DisableVsync(); }
+
+ // Override for the widget::AndroidVsync::Observer method
+ void OnVsync(const TimeStamp& aTimeStamp) override {
+ // Use the timebase from the frame callback as the vsync time, unless it
+ // is in the future.
+ TimeStamp now = TimeStamp::Now();
+ TimeStamp vsyncTime = aTimeStamp < now ? aTimeStamp : now;
+ TimeStamp outputTime = vsyncTime + GetVsyncRate();
+
+ NotifyVsync(vsyncTime, outputTime);
+ }
+
+ void OnMaybeUpdateRefreshRate() override {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() {
+ if (!self->mObservingVsync) {
+ return;
+ }
+ self->DisableVsync();
+ self->EnableVsync();
+ }));
+ }
+
+ private:
+ virtual ~AndroidVsyncSource() { DisableVsync(); }
+
+ RefPtr<widget::AndroidVsync> mAndroidVsync;
+ TimeDuration mVsyncRate;
+ bool mObservingVsync = false;
+};
+
+already_AddRefed<mozilla::gfx::VsyncSource>
+gfxAndroidPlatform::CreateGlobalHardwareVsyncSource() {
+ // Vsync was introduced since JB (API 16~18) but inaccurate. Enable only for
+ // KK (API 19) and later.
+ if (jni::GetAPIVersion() >= 19) {
+ RefPtr<AndroidVsyncSource> vsyncSource = new AndroidVsyncSource();
+ return vsyncSource.forget();
+ }
+
+ NS_WARNING("Vsync not supported. Falling back to software vsync");
+ return GetSoftwareVsyncSource();
+}