gecko-dev/gfx/thebes/gfxPlatformMac.cpp

604 строки
20 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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 "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 "nsCocoaFeatures.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;
using mozilla::dom::SystemFontListEntry;
// 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();
InitBackendPrefs(GetBackendPrefs());
if (nsCocoaFeatures::OnHighSierraOrLater()) {
mHasNativeColrFontSupport = true;
}
}
gfxPlatformMac::~gfxPlatformMac() { gfxCoreTextShaper::Shutdown(); }
BackendPrefsData gfxPlatformMac::GetBackendPrefs() const {
BackendPrefsData data;
data.mCanvasBitmask = BackendTypeBit(BackendType::SKIA);
data.mContentBitmask = BackendTypeBit(BackendType::SKIA);
data.mCanvasDefault = BackendType::SKIA;
data.mContentDefault = BackendType::SKIA;
return data;
}
bool gfxPlatformMac::UsesTiling() const {
// The non-tiling ContentClient requires CrossProcessSemaphore which
// isn't implemented for OSX.
return true;
}
bool gfxPlatformMac::ContentUsesTiling() const { return UsesTiling(); }
gfxPlatformFontList* gfxPlatformMac::CreatePlatformFontList() {
gfxPlatformFontList* list = new gfxMacPlatformFontList();
if (NS_SUCCEEDED(list->InitFontList())) {
return list;
}
gfxPlatformFontList::Shutdown();
return nullptr;
}
void gfxPlatformMac::ReadSystemFontList(
nsTArray<SystemFontListEntry>* aFontList) {
gfxMacPlatformFontList::PlatformFontList()->ReadSystemFontList(aFontList);
}
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();
}
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(uint32_t aFormatFlags) {
if (gfxPlatform::IsFontFormatSupported(aFormatFlags)) {
return true;
}
// If the generic method rejected the format hint, then check for any
// platform-specific format we know about.
if (aFormatFlags & gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT) {
return true;
}
return false;
}
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, nsACString& aSystemFontName,
gfxFontStyle& aFontStyle) {
gfxMacPlatformFontList* pfl = gfxMacPlatformFontList::PlatformFontList();
return pfl->LookupSystemFont(aSystemFontID, aSystemFontName, aFontStyle);
}
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;
}
bool gfxPlatformMac::AccelerateLayersByDefault() { return true; }
// 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() {}
Display& GetGlobalDisplay() override { return mGlobalDisplay; }
class OSXDisplay final : public VsyncSource::Display {
public:
OSXDisplay() : mDisplayLink(nullptr) {
MOZ_ASSERT(NS_IsMainThread());
mTimer = NS_NewTimer();
}
virtual ~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();
}
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.
CVReturn retval = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
// Workaround for bug 1201401: CVDisplayLinkCreateWithCGDisplays()
// (called by CVDisplayLinkCreateWithActiveCGDisplays()) sometimes
// creates a CVDisplayLinkRef with an uninitialized (nulled) internal
// pointer. If we continue to use this CVDisplayLinkRef, we will
// eventually crash in CVCGDisplayLink::getDisplayTimes(), where the
// internal pointer is dereferenced. Fortunately, when this happens
// another internal variable is also left uninitialized (zeroed),
// which is accessible via CVDisplayLinkGetCurrentCGDisplay(). In
// normal conditions the current display is never zero.
if ((retval == kCVReturnSuccess) &&
(CVDisplayLinkGetCurrentCGDisplay(mDisplayLink) == 0)) {
retval = kCVReturnInvalidDisplay;
}
if (retval != 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->InitWithNamedFuncCallback(RetryEnableVsync, this, delay,
nsITimer::TYPE_ONE_SHOT,
"RetryEnableVsync");
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);
}
}
void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (!IsVsyncEnabled()) {
return;
}
// Release the display link
if (mDisplayLink) {
CVDisplayLinkRelease(mDisplayLink);
mDisplayLink = nullptr;
}
}
bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
return mDisplayLink != nullptr;
}
TimeDuration GetVsyncRate() override { return mVsyncRate; }
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() = default;
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);
}
bool gfxPlatformMac::CheckVariationFontSupport() {
// We don't allow variation fonts to be enabled before 10.13,
// as although the Core Text APIs existed, they are known to be
// fairly buggy.
// (Note that Safari also requires 10.13 for variation-font support.)
return nsCocoaFeatures::OnHighSierraOrLater();
}