diff options
Diffstat (limited to 'gfx/thebes/gfxPlatformMac.cpp')
-rw-r--r-- | gfx/thebes/gfxPlatformMac.cpp | 617 |
1 files changed, 617 insertions, 0 deletions
diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp new file mode 100644 index 0000000000..75c5236a87 --- /dev/null +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -0,0 +1,617 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 "gfxPlatformMac.h" + +#include "gfxQuartzSurface.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/MacIOSurface.h" + +#include "gfxMacPlatformFontList.h" +#include "gfxMacFont.h" +#include "gfxCoreTextShaper.h" +#include "gfxTextRun.h" +#include "gfxUserFontSet.h" + +#include "nsTArray.h" +#include "mozilla/Preferences.h" +#include "mozilla/VsyncDispatcher.h" +#include "nsUnicodeProperties.h" +#include "qcms.h" +#include "gfx2DGlue.h" + +#include <dlfcn.h> +#include <CoreVideo/CoreVideo.h> + +#include "mozilla/layers/CompositorBridgeParent.h" +#include "VsyncSource.h" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::unicode; + +// cribbed from CTFontManager.h +enum { + kAutoActivationDisabled = 1 +}; +typedef uint32_t AutoActivationSetting; + +// bug 567552 - disable auto-activation of fonts + +static void +DisableFontActivation() +{ + // get the main bundle identifier + CFBundleRef mainBundle = ::CFBundleGetMainBundle(); + CFStringRef mainBundleID = nullptr; + + if (mainBundle) { + mainBundleID = ::CFBundleGetIdentifier(mainBundle); + } + + // bug 969388 and bug 922590 - mainBundlID as null is sometimes problematic + if (!mainBundleID) { + NS_WARNING("missing bundle ID, packaging set up incorrectly"); + return; + } + + // if possible, fetch CTFontManagerSetAutoActivationSetting + void (*CTFontManagerSetAutoActivationSettingPtr) + (CFStringRef, AutoActivationSetting); + CTFontManagerSetAutoActivationSettingPtr = + (void (*)(CFStringRef, AutoActivationSetting)) + dlsym(RTLD_DEFAULT, "CTFontManagerSetAutoActivationSetting"); + + // bug 567552 - disable auto-activation of fonts + if (CTFontManagerSetAutoActivationSettingPtr) { + CTFontManagerSetAutoActivationSettingPtr(mainBundleID, + kAutoActivationDisabled); + } +} + +gfxPlatformMac::gfxPlatformMac() +{ + DisableFontActivation(); + mFontAntiAliasingThreshold = ReadAntiAliasingThreshold(); + + uint32_t canvasMask = BackendTypeBit(BackendType::SKIA); + uint32_t contentMask = BackendTypeBit(BackendType::SKIA); + InitBackendPrefs(canvasMask, BackendType::SKIA, + contentMask, BackendType::SKIA); + + // XXX: Bug 1036682 - we run out of fds on Mac when using tiled layers because + // with 256x256 tiles we can easily hit the soft limit of 800 when using double + // buffered tiles in e10s, so let's bump the soft limit to the hard limit for the OS + // up to a new cap of OPEN_MAX. + struct rlimit limits; + if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { + limits.rlim_cur = std::min(rlim_t(OPEN_MAX), limits.rlim_max); + if (setrlimit(RLIMIT_NOFILE, &limits) != 0) { + NS_WARNING("Unable to bump RLIMIT_NOFILE to the maximum number on this OS"); + } + } + + MacIOSurfaceLib::LoadLibrary(); +} + +gfxPlatformMac::~gfxPlatformMac() +{ + gfxCoreTextShaper::Shutdown(); +} + +gfxPlatformFontList* +gfxPlatformMac::CreatePlatformFontList() +{ + gfxPlatformFontList* list = new gfxMacPlatformFontList(); + if (NS_SUCCEEDED(list->InitFontList())) { + return list; + } + gfxPlatformFontList::Shutdown(); + return nullptr; +} + +already_AddRefed<gfxASurface> +gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize, + gfxImageFormat aFormat) +{ + if (!Factory::AllowedSurfaceSize(aSize)) { + return nullptr; + } + + RefPtr<gfxASurface> newSurface = + new gfxQuartzSurface(aSize, aFormat); + return newSurface.forget(); +} + +already_AddRefed<ScaledFont> +gfxPlatformMac::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont) +{ + gfxMacFont *font = static_cast<gfxMacFont*>(aFont); + return font->GetScaledFont(aTarget); +} + +gfxFontGroup * +gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList, + const gfxFontStyle *aStyle, + gfxTextPerfMetrics* aTextPerf, + gfxUserFontSet *aUserFontSet, + gfxFloat aDevToCssSize) +{ + return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf, + aUserFontSet, aDevToCssSize); +} + +bool +gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) +{ + // check for strange format flags + NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED), + "strange font format hint set"); + + // accept supported formats + if (aFormatFlags & (gfxUserFontSet::FLAG_FORMATS_COMMON | + gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT)) { + return true; + } + + // reject all other formats, known and unknown + if (aFormatFlags != 0) { + return false; + } + + // no format hint set, need to look at data + return true; +} + +static const char kFontArialUnicodeMS[] = "Arial Unicode MS"; +static const char kFontAppleBraille[] = "Apple Braille"; +static const char kFontAppleColorEmoji[] = "Apple Color Emoji"; +static const char kFontAppleSymbols[] = "Apple Symbols"; +static const char kFontDevanagariSangamMN[] = "Devanagari Sangam MN"; +static const char kFontEuphemiaUCAS[] = "Euphemia UCAS"; +static const char kFontGeneva[] = "Geneva"; +static const char kFontGeezaPro[] = "Geeza Pro"; +static const char kFontGujaratiSangamMN[] = "Gujarati Sangam MN"; +static const char kFontGurmukhiMN[] = "Gurmukhi MN"; +static const char kFontHiraginoKakuGothic[] = "Hiragino Kaku Gothic ProN"; +static const char kFontHiraginoSansGB[] = "Hiragino Sans GB"; +static const char kFontKefa[] = "Kefa"; +static const char kFontKhmerMN[] = "Khmer MN"; +static const char kFontLaoMN[] = "Lao MN"; +static const char kFontLucidaGrande[] = "Lucida Grande"; +static const char kFontMenlo[] = "Menlo"; +static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le"; +static const char kFontMingLiUExtB[] = "MingLiU-ExtB"; +static const char kFontMyanmarMN[] = "Myanmar MN"; +static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee"; +static const char kFontSimSunExtB[] = "SimSun-ExtB"; +static const char kFontSongtiSC[] = "Songti SC"; +static const char kFontSTHeiti[] = "STHeiti"; +static const char kFontSTIXGeneral[] = "STIXGeneral"; +static const char kFontTamilMN[] = "Tamil MN"; + +void +gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, + Script aRunScript, + nsTArray<const char*>& aFontList) +{ + EmojiPresentation emoji = GetEmojiPresentation(aCh); + if (emoji != EmojiPresentation::TextOnly) { + if (aNextCh == kVariationSelector16 || + (aNextCh != kVariationSelector15 && + emoji == EmojiPresentation::EmojiDefault)) { + // if char is followed by VS16, try for a color emoji glyph + aFontList.AppendElement(kFontAppleColorEmoji); + } + } + + aFontList.AppendElement(kFontLucidaGrande); + + if (!IS_IN_BMP(aCh)) { + uint32_t p = aCh >> 16; + if (p == 1) { + aFontList.AppendElement(kFontAppleSymbols); + aFontList.AppendElement(kFontSTIXGeneral); + aFontList.AppendElement(kFontGeneva); + } else if (p == 2) { + // OSX installations with MS Office may have these fonts + aFontList.AppendElement(kFontMingLiUExtB); + aFontList.AppendElement(kFontSimSunExtB); + } + } else { + uint32_t b = (aCh >> 8) & 0xff; + + switch (b) { + case 0x03: + case 0x05: + aFontList.AppendElement(kFontGeneva); + break; + case 0x07: + aFontList.AppendElement(kFontGeezaPro); + break; + case 0x09: + aFontList.AppendElement(kFontDevanagariSangamMN); + break; + case 0x0a: + aFontList.AppendElement(kFontGurmukhiMN); + aFontList.AppendElement(kFontGujaratiSangamMN); + break; + case 0x0b: + aFontList.AppendElement(kFontTamilMN); + break; + case 0x0e: + aFontList.AppendElement(kFontLaoMN); + break; + case 0x0f: + aFontList.AppendElement(kFontSongtiSC); + break; + case 0x10: + aFontList.AppendElement(kFontMenlo); + aFontList.AppendElement(kFontMyanmarMN); + break; + case 0x13: // Cherokee + aFontList.AppendElement(kFontPlantagenetCherokee); + aFontList.AppendElement(kFontKefa); + break; + case 0x14: // Unified Canadian Aboriginal Syllabics + case 0x15: + case 0x16: + aFontList.AppendElement(kFontEuphemiaUCAS); + aFontList.AppendElement(kFontGeneva); + break; + case 0x18: // Mongolian, UCAS + aFontList.AppendElement(kFontSTHeiti); + aFontList.AppendElement(kFontEuphemiaUCAS); + break; + case 0x19: // Khmer + aFontList.AppendElement(kFontKhmerMN); + aFontList.AppendElement(kFontMicrosoftTaiLe); + break; + case 0x1d: + case 0x1e: + aFontList.AppendElement(kFontGeneva); + break; + case 0x20: // Symbol ranges + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2e: + aFontList.AppendElement(kFontHiraginoKakuGothic); + aFontList.AppendElement(kFontAppleSymbols); + aFontList.AppendElement(kFontMenlo); + aFontList.AppendElement(kFontSTIXGeneral); + aFontList.AppendElement(kFontGeneva); + aFontList.AppendElement(kFontAppleColorEmoji); + break; + case 0x2c: + aFontList.AppendElement(kFontGeneva); + break; + case 0x2d: + aFontList.AppendElement(kFontKefa); + aFontList.AppendElement(kFontGeneva); + break; + case 0x28: // Braille + aFontList.AppendElement(kFontAppleBraille); + break; + case 0x31: + aFontList.AppendElement(kFontHiraginoSansGB); + break; + case 0x4d: + aFontList.AppendElement(kFontAppleSymbols); + break; + case 0xa0: // Yi + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + aFontList.AppendElement(kFontSTHeiti); + break; + case 0xa6: + case 0xa7: + aFontList.AppendElement(kFontGeneva); + aFontList.AppendElement(kFontAppleSymbols); + break; + case 0xab: + aFontList.AppendElement(kFontKefa); + break; + case 0xfc: + case 0xff: + aFontList.AppendElement(kFontAppleSymbols); + break; + default: + break; + } + } + + // Arial Unicode MS has lots of glyphs for obscure, use it as a last resort + aFontList.AppendElement(kFontArialUnicodeMS); +} + +/*static*/ void +gfxPlatformMac::LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID, + nsAString& aSystemFontName, + gfxFontStyle& aFontStyle, + float aDevPixPerCSSPixel) +{ + gfxMacPlatformFontList* pfl = gfxMacPlatformFontList::PlatformFontList(); + return pfl->LookupSystemFont(aSystemFontID, aSystemFontName, aFontStyle, + aDevPixPerCSSPixel); +} + +uint32_t +gfxPlatformMac::ReadAntiAliasingThreshold() +{ + uint32_t threshold = 0; // default == no threshold + + // first read prefs flag to determine whether to use the setting or not + bool useAntiAliasingThreshold = Preferences::GetBool("gfx.use_text_smoothing_setting", false); + + // if the pref setting is disabled, return 0 which effectively disables this feature + if (!useAntiAliasingThreshold) + return threshold; + + // value set via Appearance pref panel, "Turn off text smoothing for font sizes xxx and smaller" + CFNumberRef prefValue = (CFNumberRef)CFPreferencesCopyAppValue(CFSTR("AppleAntiAliasingThreshold"), kCFPreferencesCurrentApplication); + + if (prefValue) { + if (!CFNumberGetValue(prefValue, kCFNumberIntType, &threshold)) { + threshold = 0; + } + CFRelease(prefValue); + } + + return threshold; +} + +// This is the renderer output callback function, called on the vsync thread +static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink, + const CVTimeStamp* aNow, + const CVTimeStamp* aOutputTime, + CVOptionFlags aFlagsIn, + CVOptionFlags* aFlagsOut, + void* aDisplayLinkContext); + +class OSXVsyncSource final : public VsyncSource +{ +public: + OSXVsyncSource() + { + } + + virtual Display& GetGlobalDisplay() override + { + return mGlobalDisplay; + } + + class OSXDisplay final : public VsyncSource::Display + { + public: + OSXDisplay() + : mDisplayLink(nullptr) + { + MOZ_ASSERT(NS_IsMainThread()); + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + } + + ~OSXDisplay() + { + MOZ_ASSERT(NS_IsMainThread()); + } + + static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay) + { + MOZ_ASSERT(NS_IsMainThread()); + OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay); + MOZ_ASSERT(osxDisplay); + osxDisplay->EnableVsync(); + } + + virtual void EnableVsync() override + { + MOZ_ASSERT(NS_IsMainThread()); + if (IsVsyncEnabled()) { + return; + } + + // Create a display link capable of being used with all active displays + // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor + // situations. According to the docs, it is compatible with all displays running on the computer + // But if we have different monitors at different display rates, we may hit issues. + if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) { + NS_WARNING("Could not create a display link with all active displays. Retrying"); + CVDisplayLinkRelease(mDisplayLink); + mDisplayLink = nullptr; + + // bug 1142708 - When coming back from sleep, + // or when changing displays, active displays may not be ready yet, + // even if listening for the kIOMessageSystemHasPoweredOn event + // from OS X sleep notifications. + // Active displays are those that are drawable. + // bug 1144638 - When changing display configurations and getting + // notifications from CGDisplayReconfigurationCallBack, the + // callback gets called twice for each active display + // so it's difficult to know when all displays are active. + // Instead, try again soon. The delay is arbitrary. 100ms chosen + // because on a late 2013 15" retina, it takes about that + // long to come back up from sleep. + uint32_t delay = 100; + mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT); + return; + } + + if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) { + NS_WARNING("Could not set displaylink output callback"); + CVDisplayLinkRelease(mDisplayLink); + mDisplayLink = nullptr; + return; + } + + mPreviousTimestamp = TimeStamp::Now(); + if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) { + NS_WARNING("Could not activate the display link"); + CVDisplayLinkRelease(mDisplayLink); + mDisplayLink = nullptr; + } + + CVTime vsyncRate = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(mDisplayLink); + if (vsyncRate.flags & kCVTimeIsIndefinite) { + NS_WARNING("Could not get vsync rate, setting to 60."); + mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0); + } else { + int64_t timeValue = vsyncRate.timeValue; + int64_t timeScale = vsyncRate.timeScale; + const int milliseconds = 1000; + float rateInMs = ((double) timeValue / (double) timeScale) * milliseconds; + mVsyncRate = TimeDuration::FromMilliseconds(rateInMs); + } + } + + virtual void DisableVsync() override + { + MOZ_ASSERT(NS_IsMainThread()); + if (!IsVsyncEnabled()) { + return; + } + + // Release the display link + if (mDisplayLink) { + CVDisplayLinkRelease(mDisplayLink); + mDisplayLink = nullptr; + } + } + + virtual bool IsVsyncEnabled() override + { + MOZ_ASSERT(NS_IsMainThread()); + return mDisplayLink != nullptr; + } + + virtual TimeDuration GetVsyncRate() override + { + return mVsyncRate; + } + + virtual void Shutdown() override + { + MOZ_ASSERT(NS_IsMainThread()); + mTimer->Cancel(); + mTimer = nullptr; + DisableVsync(); + } + + // The vsync timestamps given by the CVDisplayLinkCallback are + // in the future for the NEXT frame. Large parts of Gecko, such + // as animations assume a timestamp at either now or in the past. + // Normalize the timestamps given to the VsyncDispatchers to the vsync + // that just occured, not the vsync that is upcoming. + TimeStamp mPreviousTimestamp; + + private: + // Manages the display link render thread + CVDisplayLinkRef mDisplayLink; + RefPtr<nsITimer> mTimer; + TimeDuration mVsyncRate; + }; // OSXDisplay + +private: + virtual ~OSXVsyncSource() + { + } + + OSXDisplay mGlobalDisplay; +}; // OSXVsyncSource + +static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink, + const CVTimeStamp* aNow, + const CVTimeStamp* aOutputTime, + CVOptionFlags aFlagsIn, + CVOptionFlags* aFlagsOut, + void* aDisplayLinkContext) +{ + // Executed on OS X hardware vsync thread + OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext; + int64_t nextVsyncTimestamp = aOutputTime->hostTime; + + mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp); + mozilla::TimeStamp previousVsync = display->mPreviousTimestamp; + mozilla::TimeStamp now = TimeStamp::Now(); + + // Snow leopard sometimes sends vsync timestamps very far in the past. + // Normalize the vsync timestamps to now. + if (nextVsync <= previousVsync) { + nextVsync = now; + previousVsync = now; + } else if (now < previousVsync) { + // Bug 1158321 - The VsyncCallback can sometimes execute before the reported + // vsync time. In those cases, normalize the timestamp to Now() as sending + // timestamps in the future has undefined behavior. See the comment above + // OSXDisplay::mPreviousTimestamp + previousVsync = now; + } + + display->mPreviousTimestamp = nextVsync; + + display->NotifyVsync(previousVsync); + return kCVReturnSuccess; +} + +already_AddRefed<mozilla::gfx::VsyncSource> +gfxPlatformMac::CreateHardwareVsyncSource() +{ + RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource(); + VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay(); + primaryDisplay.EnableVsync(); + if (!primaryDisplay.IsVsyncEnabled()) { + NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync."); + return gfxPlatform::CreateHardwareVsyncSource(); + } + + primaryDisplay.DisableVsync(); + return osxVsyncSource.forget(); +} + +void +gfxPlatformMac::GetPlatformCMSOutputProfile(void* &mem, size_t &size) +{ + mem = nullptr; + size = 0; + + CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID()); + if (!cspace) { + cspace = ::CGColorSpaceCreateDeviceRGB(); + } + if (!cspace) { + return; + } + + CFDataRef iccp = ::CGColorSpaceCopyICCProfile(cspace); + + ::CFRelease(cspace); + + if (!iccp) { + return; + } + + // copy to external buffer + size = static_cast<size_t>(::CFDataGetLength(iccp)); + if (size > 0) { + void *data = malloc(size); + if (data) { + memcpy(data, ::CFDataGetBytePtr(iccp), size); + mem = data; + } else { + size = 0; + } + } + + ::CFRelease(iccp); +} |