import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

15
gfx/src/AppUnits.h Normal file
View file

@ -0,0 +1,15 @@
/* -*- Mode: C++; 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/. */
#ifndef mozilla_AppUnits_h
#define mozilla_AppUnits_h
#include <stdint.h>
namespace mozilla {
inline int32_t AppUnitsPerCSSPixel() { return 60; }
inline int32_t AppUnitsPerCSSInch() { return 96 * AppUnitsPerCSSPixel(); }
} // namespace mozilla
#endif /* _NS_APPUNITS_H_ */

50
gfx/src/ArrayView.h Normal file
View file

@ -0,0 +1,50 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_ARRAY_VIEW_H_
#define MOZILLA_GFX_ARRAY_VIEW_H_
#include "nsTArray.h"
/* This is similar to mfbt/Range.h but has implicit conversion
* from nsTArray and less bounds checking.
* For now, prefer Range over ArrayView */
namespace mozilla {
namespace gfx {
template<typename T>
class ArrayView
{
public:
MOZ_IMPLICIT ArrayView(const nsTArray<T>& aData) :
mData(aData.Elements()), mLength(aData.Length())
{
}
ArrayView(const T* aData, const size_t aLength) :
mData(aData), mLength(aLength)
{
}
const T& operator[](const size_t aIdx) const
{
return mData[aIdx];
}
size_t Length() const
{
return mLength;
}
const T* Data() const
{
return mData;
}
private:
const T* mData;
const size_t mLength;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_ARRAY_VIEW_H_ */

View file

@ -0,0 +1,620 @@
/* -*- Mode: C++; tab-width: 2; 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 "DriverCrashGuard.h"
#include "gfxEnv.h"
#include "gfxPrefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Services.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/dom/ContentChild.h"
namespace mozilla {
namespace gfx {
static const size_t NUM_CRASH_GUARD_TYPES = size_t(CrashGuardType::NUM_TYPES);
static const char* sCrashGuardNames[] = {
"d3d11layers",
"d3d9video",
"glcontext",
"d3d11video",
};
static_assert(MOZ_ARRAY_LENGTH(sCrashGuardNames) == NUM_CRASH_GUARD_TYPES,
"CrashGuardType updated without a name string");
static inline void
BuildCrashGuardPrefName(CrashGuardType aType, nsCString& aOutPrefName)
{
MOZ_ASSERT(aType < CrashGuardType::NUM_TYPES);
MOZ_ASSERT(sCrashGuardNames[size_t(aType)]);
aOutPrefName.Assign("gfx.crash-guard.status.");
aOutPrefName.Append(sCrashGuardNames[size_t(aType)]);
}
DriverCrashGuard::DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent)
: mType(aType)
, mMode(aContentParent ? Mode::Proxy : Mode::Normal)
, mInitialized(false)
, mGuardActivated(false)
, mCrashDetected(false)
{
BuildCrashGuardPrefName(aType, mStatusPref);
}
void
DriverCrashGuard::InitializeIfNeeded()
{
if (mInitialized) {
return;
}
mInitialized = true;
Initialize();
}
static inline bool
AreCrashGuardsEnabled()
{
// Crash guard isn't supported in the GPU process since the entire
// process is basically a crash guard.
if (XRE_IsGPUProcess()) {
return false;
}
#ifdef NIGHTLY_BUILD
// We only use the crash guard on non-nightly channels, since the nightly
// channel is for development and having graphics features perma-disabled
// is rather annoying. Unless the user forces is with an environment
// variable, which comes in handy for testing.
return gfxEnv::ForceCrashGuardNightly();
#else
// Check to see if all guards have been disabled through the environment.
if (gfxEnv::DisableCrashGuard()) {
return false;
}
return true;
#endif
}
void
DriverCrashGuard::Initialize()
{
if (!AreCrashGuardsEnabled()) {
return;
}
// Using DriverCrashGuard off the main thread currently does not work. Under
// e10s it could conceivably work by dispatching the IPC calls via the main
// thread. In the parent process this would be harder. For now, we simply
// exit early instead.
if (!NS_IsMainThread()) {
return;
}
mGfxInfo = services::GetGfxInfo();
if (XRE_IsContentProcess()) {
// Ask the parent whether or not activating the guard is okay. The parent
// won't bother if it detected a crash.
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
cc->SendBeginDriverCrashGuard(uint32_t(mType), &mCrashDetected);
if (mCrashDetected) {
LogFeatureDisabled();
return;
}
ActivateGuard();
return;
}
// Always check whether or not the lock file exists. For example, we could
// have crashed creating a D3D9 device in the parent process, and on restart
// are now requesting one in the child process. We catch everything here.
if (RecoverFromCrash()) {
mCrashDetected = true;
return;
}
// If the environment has changed, we always activate the guard. In the
// parent process this performs main-thread disk I/O. Child process guards
// only incur an IPC cost, so if we're proxying for a child process, we
// play it safe and activate the guard as long as we don't expect it to
// crash.
if (CheckOrRefreshEnvironment() ||
(mMode == Mode::Proxy && GetStatus() != DriverInitStatus::Crashed))
{
ActivateGuard();
return;
}
// If we got here and our status is "crashed", then the environment has not
// updated and we do not want to attempt to use the driver again.
if (GetStatus() == DriverInitStatus::Crashed) {
mCrashDetected = true;
LogFeatureDisabled();
}
}
DriverCrashGuard::~DriverCrashGuard()
{
if (!mGuardActivated) {
return;
}
if (XRE_IsParentProcess()) {
if (mGuardFile) {
mGuardFile->Remove(false);
}
// If during our initialization, no other process encountered a crash, we
// proceed to mark the status as okay.
if (GetStatus() != DriverInitStatus::Crashed) {
SetStatus(DriverInitStatus::Okay);
}
} else {
dom::ContentChild::GetSingleton()->SendEndDriverCrashGuard(uint32_t(mType));
}
#ifdef MOZ_CRASHREPORTER
// Remove the crash report annotation.
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"),
NS_LITERAL_CSTRING(""));
#endif
}
bool
DriverCrashGuard::Crashed()
{
InitializeIfNeeded();
// Note, we read mCrashDetected instead of GetStatus(), since in child
// processes we're not guaranteed that the prefs have been synced in
// time.
return mCrashDetected;
}
nsCOMPtr<nsIFile>
DriverCrashGuard::GetGuardFile()
{
MOZ_ASSERT(XRE_IsParentProcess());
nsCString filename;
filename.Assign(sCrashGuardNames[size_t(mType)]);
filename.Append(".guard");
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(file));
if (!file) {
return nullptr;
}
if (!NS_SUCCEEDED(file->AppendNative(filename))) {
return nullptr;
}
return file;
}
void
DriverCrashGuard::ActivateGuard()
{
mGuardActivated = true;
#ifdef MOZ_CRASHREPORTER
// Anotate crash reports only if we're a real guard. Otherwise, we could
// attribute a random parent process crash to a graphics problem in a child
// process.
if (mMode != Mode::Proxy) {
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"),
NS_LITERAL_CSTRING("1"));
}
#endif
// If we're in the content process, the rest of the guarding is handled
// in the parent.
if (XRE_IsContentProcess()) {
return;
}
SetStatus(DriverInitStatus::Attempting);
if (mMode != Mode::Proxy) {
// In parent process guards, we use two tombstones to detect crashes: a
// preferences and a zero-byte file on the filesystem.
FlushPreferences();
// Create a temporary tombstone/lockfile.
FILE* fp = nullptr;
mGuardFile = GetGuardFile();
if (!mGuardFile || !NS_SUCCEEDED(mGuardFile->OpenANSIFileDesc("w", &fp))) {
return;
}
fclose(fp);
}
}
void
DriverCrashGuard::NotifyCrashed()
{
CheckOrRefreshEnvironment();
SetStatus(DriverInitStatus::Crashed);
FlushPreferences();
LogCrashRecovery();
}
bool
DriverCrashGuard::RecoverFromCrash()
{
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIFile> file = GetGuardFile();
bool exists;
if ((file &&
NS_SUCCEEDED(file->Exists(&exists)) &&
exists) ||
(GetStatus() == DriverInitStatus::Attempting))
{
// If we get here, we've just recovered from a crash. Disable acceleration
// until the environment changes.
if (file) {
file->Remove(false);
}
NotifyCrashed();
return true;
}
return false;
}
// Return true if the caller should proceed to guard for crashes. False if
// the environment has not changed. We persist the "changed" status across
// calls, so that after an environment changes, all guards for the new
// session are activated rather than just the first.
bool
DriverCrashGuard::CheckOrRefreshEnvironment()
{
// Our result can be cached statically since we don't check live prefs.
static bool sBaseInfoChanged = false;
static bool sBaseInfoChecked = false;
if (!sBaseInfoChecked) {
// None of the prefs we care about, so we cache the result statically.
sBaseInfoChecked = true;
sBaseInfoChanged = UpdateBaseEnvironment();
}
// Always update the full environment, even if the base info didn't change.
return UpdateEnvironment() ||
sBaseInfoChanged ||
GetStatus() == DriverInitStatus::Unknown;
}
bool
DriverCrashGuard::UpdateBaseEnvironment()
{
bool changed = false;
if (mGfxInfo) {
nsString value;
// Driver properties.
mGfxInfo->GetAdapterDriverVersion(value);
changed |= CheckAndUpdatePref("driverVersion", value);
mGfxInfo->GetAdapterDeviceID(value);
changed |= CheckAndUpdatePref("deviceID", value);
}
// Firefox properties.
changed |= CheckAndUpdatePref("appVersion", NS_LITERAL_STRING(MOZ_APP_VERSION));
return changed;
}
bool
DriverCrashGuard::FeatureEnabled(int aFeature, bool aDefault)
{
if (!mGfxInfo) {
return aDefault;
}
int32_t status;
nsCString discardFailureId;
if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, discardFailureId, &status))) {
return false;
}
return status == nsIGfxInfo::FEATURE_STATUS_OK;
}
bool
DriverCrashGuard::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue)
{
std::string pref = GetFullPrefName(aPrefName);
bool oldValue;
if (NS_SUCCEEDED(Preferences::GetBool(pref.c_str(), &oldValue)) &&
oldValue == aCurrentValue)
{
return false;
}
Preferences::SetBool(pref.c_str(), aCurrentValue);
return true;
}
bool
DriverCrashGuard::CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue)
{
std::string pref = GetFullPrefName(aPrefName);
nsAdoptingString oldValue = Preferences::GetString(pref.c_str());
if (oldValue == aCurrentValue) {
return false;
}
Preferences::SetString(pref.c_str(), aCurrentValue);
return true;
}
std::string
DriverCrashGuard::GetFullPrefName(const char* aPref)
{
return std::string("gfx.crash-guard.") +
std::string(sCrashGuardNames[uint32_t(mType)]) +
std::string(".") +
std::string(aPref);
}
DriverInitStatus
DriverCrashGuard::GetStatus() const
{
return (DriverInitStatus)Preferences::GetInt(mStatusPref.get(), 0);
}
void
DriverCrashGuard::SetStatus(DriverInitStatus aStatus)
{
MOZ_ASSERT(XRE_IsParentProcess());
Preferences::SetInt(mStatusPref.get(), int32_t(aStatus));
}
void
DriverCrashGuard::FlushPreferences()
{
MOZ_ASSERT(XRE_IsParentProcess());
if (nsIPrefService* prefService = Preferences::GetService()) {
prefService->SavePrefFile(nullptr);
}
}
void
DriverCrashGuard::ForEachActiveCrashGuard(const CrashGuardCallback& aCallback)
{
if (!AreCrashGuardsEnabled()) {
// Even if guards look active (via prefs), they can be ignored if globally
// disabled.
return;
}
for (size_t i = 0; i < NUM_CRASH_GUARD_TYPES; i++) {
CrashGuardType type = static_cast<CrashGuardType>(i);
nsCString prefName;
BuildCrashGuardPrefName(type, prefName);
auto status =
static_cast<DriverInitStatus>(Preferences::GetInt(prefName.get(), 0));
if (status != DriverInitStatus::Crashed) {
continue;
}
aCallback(sCrashGuardNames[i], prefName.get());
}
}
D3D11LayersCrashGuard::D3D11LayersCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::D3D11Layers, aContentParent)
{
}
void
D3D11LayersCrashGuard::Initialize()
{
if (!XRE_IsParentProcess()) {
// We assume the parent process already performed crash detection for
// graphics devices.
return;
}
DriverCrashGuard::Initialize();
// If no telemetry states have been recorded, this will set the state to okay.
// Otherwise, it will have no effect.
RecordTelemetry(TelemetryState::Okay);
}
bool
D3D11LayersCrashGuard::UpdateEnvironment()
{
// Our result can be cached statically since we don't check live prefs.
static bool checked = false;
static bool changed = false;
if (checked) {
return changed;
}
checked = true;
// Feature status.
#if defined(XP_WIN)
bool d2dEnabled = gfxPrefs::Direct2DForceEnabled() ||
(!gfxPrefs::Direct2DDisabled() && FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT2D));
changed |= CheckAndUpdateBoolPref("feature-d2d", d2dEnabled);
bool d3d11Enabled = !gfxPrefs::LayersPreferD3D9();
if (!FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS)) {
d3d11Enabled = false;
}
changed |= CheckAndUpdateBoolPref("feature-d3d11", d3d11Enabled);
#endif
if (!changed) {
return false;
}
RecordTelemetry(TelemetryState::EnvironmentChanged);
return true;
}
void
D3D11LayersCrashGuard::LogCrashRecovery()
{
RecordTelemetry(TelemetryState::RecoveredFromCrash);
gfxCriticalNote << "D3D11 layers just crashed; D3D11 will be disabled.";
}
void
D3D11LayersCrashGuard::LogFeatureDisabled()
{
RecordTelemetry(TelemetryState::FeatureDisabled);
gfxCriticalNote << "D3D11 layers disabled due to a prior crash.";
}
void
D3D11LayersCrashGuard::RecordTelemetry(TelemetryState aState)
{
// D3D11LayersCrashGuard is a no-op in the child process.
if (!XRE_IsParentProcess()) {
return;
}
// Since we instantiate this class more than once, make sure we only record
// the first state (since that is really all we care about).
static bool sTelemetryStateRecorded = false;
if (sTelemetryStateRecorded) {
return;
}
Telemetry::Accumulate(Telemetry::GRAPHICS_DRIVER_STARTUP_TEST, int32_t(aState));
sTelemetryStateRecorded = true;
}
D3D9VideoCrashGuard::D3D9VideoCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::D3D9Video, aContentParent)
{
}
bool
D3D9VideoCrashGuard::UpdateEnvironment()
{
// We don't care about any extra preferences here.
return false;
}
void
D3D9VideoCrashGuard::LogCrashRecovery()
{
gfxCriticalNote << "DXVA2D3D9 just crashed; hardware video will be disabled.";
}
void
D3D9VideoCrashGuard::LogFeatureDisabled()
{
gfxCriticalNote << "DXVA2D3D9 video decoding is disabled due to a previous crash.";
}
D3D11VideoCrashGuard::D3D11VideoCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::D3D11Video, aContentParent)
{
}
bool
D3D11VideoCrashGuard::UpdateEnvironment()
{
// We don't care about any extra preferences here.
return false;
}
void
D3D11VideoCrashGuard::LogCrashRecovery()
{
gfxCriticalNote << "DXVA2D3D11 just crashed; hardware video will be disabled.";
}
void
D3D11VideoCrashGuard::LogFeatureDisabled()
{
gfxCriticalNote << "DXVA2D3D11 video decoding is disabled due to a previous crash.";
}
GLContextCrashGuard::GLContextCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::GLContext, aContentParent)
{
}
void
GLContextCrashGuard::Initialize()
{
if (XRE_IsContentProcess()) {
// Disable the GL crash guard in content processes, since we're not going
// to lose the entire browser and we don't want to hinder WebGL availability.
return;
}
#if defined(MOZ_WIDGET_ANDROID)
// Disable the WebGL crash guard on Android - it doesn't use E10S, and
// its drivers will essentially never change, so the crash guard could
// permanently disable WebGL.
return;
#endif
DriverCrashGuard::Initialize();
}
bool
GLContextCrashGuard::UpdateEnvironment()
{
static bool checked = false;
static bool changed = false;
if (checked) {
return changed;
}
checked = true;
#if defined(XP_WIN)
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-force-d3d11",
gfxPrefs::WebGLANGLEForceD3D11());
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-try-d3d11",
gfxPrefs::WebGLANGLETryD3D11());
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-force-warp",
gfxPrefs::WebGLANGLEForceWARP());
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle",
FeatureEnabled(nsIGfxInfo::FEATURE_WEBGL_ANGLE, false));
changed |= CheckAndUpdateBoolPref("gfx.driver-init.direct3d11-angle",
FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, false));
#endif
return changed;
}
void
GLContextCrashGuard::LogCrashRecovery()
{
gfxCriticalNote << "GLContext just crashed.";
}
void
GLContextCrashGuard::LogFeatureDisabled()
{
gfxCriticalNote << "GLContext remains enabled despite a previous crash.";
}
} // namespace gfx
} // namespace mozilla

182
gfx/src/DriverCrashGuard.h Normal file
View file

@ -0,0 +1,182 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef gfx_src_DriverCrashGuard_h__
#define gfx_src_DriverCrashGuard_h__
#include "nsCOMPtr.h"
#include "nsIGfxInfo.h"
#include "nsIFile.h"
#include "nsString.h"
#include "mozilla/Function.h"
#include <string>
namespace mozilla {
namespace dom {
class ContentParent;
} // namespace dom
namespace gfx {
enum class DriverInitStatus
{
// Drivers have not been initialized yet.
Unknown,
// We're attempting to initialize drivers.
Attempting,
// Drivers were successfully initialized last run.
Okay,
// We crashed during driver initialization, and have restarted.
Crashed
};
enum class CrashGuardType : uint32_t
{
D3D11Layers,
D3D9Video,
GLContext,
D3D11Video,
// Add new entries above this line, update the name array in
// DriverCrashGuard.cpp, and make sure to add an entry in
// ContentParent.cpp.
NUM_TYPES
};
// DriverCrashGuard is used to detect crashes at graphics driver callsites.
//
// If the graphics environment is unrecognized or has changed since the last
// session, the crash guard will activate and will detect any crashes within
// the scope of the guard object.
//
// If a callsite has a previously encountered crash, and the environment has
// not changed since the last session, then the guard will set a status flag
// indicating that the driver should not be used.
class DriverCrashGuard
{
public:
DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent);
virtual ~DriverCrashGuard();
bool Crashed();
void NotifyCrashed();
// These are the values reported to Telemetry (GRAPHICS_DRIVER_STARTUP_TEST).
// Values should not change; add new values to the end.
enum class TelemetryState {
Okay = 0,
EnvironmentChanged = 1,
RecoveredFromCrash = 2,
FeatureDisabled = 3
};
enum class Mode {
// Normal operation.
Normal,
// Acting as a proxy between the parent and child process.
Proxy
};
typedef mozilla::function<void(const char* aName, const char* aPrefName)>
CrashGuardCallback;
static void ForEachActiveCrashGuard(const CrashGuardCallback& aCallback);
protected:
virtual void Initialize();
virtual bool UpdateEnvironment() = 0;
virtual void LogCrashRecovery() = 0;
virtual void LogFeatureDisabled() = 0;
// Helper functions.
bool FeatureEnabled(int aFeature, bool aDefault=true);
bool CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue);
bool CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue);
std::string GetFullPrefName(const char* aPref);
private:
// Either process.
void InitializeIfNeeded();
bool CheckOrRefreshEnvironment();
bool UpdateBaseEnvironment();
DriverInitStatus GetStatus() const;
// Parent process only.
nsCOMPtr<nsIFile> GetGuardFile();
bool RecoverFromCrash();
void ActivateGuard();
void FlushPreferences();
void SetStatus(DriverInitStatus aStatus);
private:
CrashGuardType mType;
Mode mMode;
bool mInitialized;
bool mGuardActivated;
bool mCrashDetected;
nsCOMPtr<nsIFile> mGuardFile;
protected:
nsCString mStatusPref;
nsCOMPtr<nsIGfxInfo> mGfxInfo;
};
class D3D11LayersCrashGuard final : public DriverCrashGuard
{
public:
explicit D3D11LayersCrashGuard(dom::ContentParent* aContentParent = nullptr);
protected:
void Initialize() override;
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
private:
void RecordTelemetry(TelemetryState aState);
};
class D3D9VideoCrashGuard final : public DriverCrashGuard
{
public:
explicit D3D9VideoCrashGuard(dom::ContentParent* aContentParent = nullptr);
protected:
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
};
class D3D11VideoCrashGuard final : public DriverCrashGuard
{
public:
explicit D3D11VideoCrashGuard(dom::ContentParent* aContentParent = nullptr);
protected:
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
};
class GLContextCrashGuard final : public DriverCrashGuard
{
public:
explicit GLContextCrashGuard(dom::ContentParent* aContentParent = nullptr);
void Initialize() override;
protected:
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
};
} // namespace gfx
} // namespace mozilla
#endif // gfx_src_DriverCrashGuard_h__

2185
gfx/src/FilterSupport.cpp Normal file

File diff suppressed because it is too large Load diff

479
gfx/src/FilterSupport.h Normal file
View file

@ -0,0 +1,479 @@
/* -*- 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/. */
#ifndef __FilterSupport_h
#define __FilterSupport_h
#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/gfx/2D.h"
#include "nsClassHashtable.h"
#include "nsTArray.h"
#include "nsRegion.h"
namespace mozilla {
namespace gfx {
// Morphology Operators
const unsigned short SVG_OPERATOR_UNKNOWN = 0;
const unsigned short SVG_OPERATOR_ERODE = 1;
const unsigned short SVG_OPERATOR_DILATE = 2;
// ColorMatrix types
const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
const unsigned short SVG_FECOLORMATRIX_TYPE_HUE_ROTATE = 3;
const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA = 4;
// ColorMatrix types for CSS filters
const unsigned short SVG_FECOLORMATRIX_TYPE_SEPIA = 5;
// ComponentTransfer types
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0;
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1;
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE = 2;
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3;
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR = 4;
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA = 5;
// Blend Mode Values
const unsigned short SVG_FEBLEND_MODE_UNKNOWN = 0;
const unsigned short SVG_FEBLEND_MODE_NORMAL = 1;
const unsigned short SVG_FEBLEND_MODE_MULTIPLY = 2;
const unsigned short SVG_FEBLEND_MODE_SCREEN = 3;
const unsigned short SVG_FEBLEND_MODE_DARKEN = 4;
const unsigned short SVG_FEBLEND_MODE_LIGHTEN = 5;
const unsigned short SVG_FEBLEND_MODE_OVERLAY = 6;
const unsigned short SVG_FEBLEND_MODE_COLOR_DODGE = 7;
const unsigned short SVG_FEBLEND_MODE_COLOR_BURN = 8;
const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
const unsigned short SVG_FEBLEND_MODE_HUE = 13;
const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
// Edge Mode Values
const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
const unsigned short SVG_EDGEMODE_DUPLICATE = 1;
const unsigned short SVG_EDGEMODE_WRAP = 2;
const unsigned short SVG_EDGEMODE_NONE = 3;
// Channel Selectors
const unsigned short SVG_CHANNEL_UNKNOWN = 0;
const unsigned short SVG_CHANNEL_R = 1;
const unsigned short SVG_CHANNEL_G = 2;
const unsigned short SVG_CHANNEL_B = 3;
const unsigned short SVG_CHANNEL_A = 4;
// Turbulence Types
const unsigned short SVG_TURBULENCE_TYPE_UNKNOWN = 0;
const unsigned short SVG_TURBULENCE_TYPE_FRACTALNOISE = 1;
const unsigned short SVG_TURBULENCE_TYPE_TURBULENCE = 2;
// Composite Operators
const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
enum AttributeName {
eBlendBlendmode = 0,
eMorphologyRadii,
eMorphologyOperator,
eColorMatrixType,
eColorMatrixValues,
eFloodColor,
eTileSourceRect,
eComponentTransferFunctionR,
eComponentTransferFunctionG,
eComponentTransferFunctionB,
eComponentTransferFunctionA,
eComponentTransferFunctionType,
eComponentTransferFunctionTableValues,
eComponentTransferFunctionSlope,
eComponentTransferFunctionIntercept,
eComponentTransferFunctionAmplitude,
eComponentTransferFunctionExponent,
eComponentTransferFunctionOffset,
eConvolveMatrixKernelSize,
eConvolveMatrixKernelMatrix,
eConvolveMatrixDivisor,
eConvolveMatrixBias,
eConvolveMatrixTarget,
eConvolveMatrixEdgeMode,
eConvolveMatrixKernelUnitLength,
eConvolveMatrixPreserveAlpha,
eOffsetOffset,
eDropShadowStdDeviation,
eDropShadowOffset,
eDropShadowColor,
eDisplacementMapScale,
eDisplacementMapXChannel,
eDisplacementMapYChannel,
eTurbulenceOffset,
eTurbulenceBaseFrequency,
eTurbulenceNumOctaves,
eTurbulenceSeed,
eTurbulenceStitchable,
eTurbulenceType,
eCompositeOperator,
eCompositeCoefficients,
eGaussianBlurStdDeviation,
eLightingLight,
eLightingSurfaceScale,
eLightingKernelUnitLength,
eLightingColor,
eDiffuseLightingDiffuseConstant,
eSpecularLightingSpecularConstant,
eSpecularLightingSpecularExponent,
eLightType,
eLightTypeNone,
eLightTypePoint,
eLightTypeSpot,
eLightTypeDistant,
ePointLightPosition,
eSpotLightPosition,
eSpotLightPointsAt,
eSpotLightFocus,
eSpotLightLimitingConeAngle,
eDistantLightAzimuth,
eDistantLightElevation,
eImageInputIndex,
eImageFilter,
eImageNativeSize,
eImageSubregion,
eImageTransform,
eLastAttributeName
};
class DrawTarget;
class SourceSurface;
struct FilterAttribute;
enum class AttributeType {
eBool,
eUint,
eFloat,
eSize,
eIntSize,
eIntPoint,
eMatrix,
eMatrix5x4,
ePoint3D,
eColor,
eAttributeMap,
eFloats,
Max
};
// Limits
const float kMaxStdDeviation = 500;
// A class that stores values of different types, keyed by an attribute name.
// The Get*() methods assert that they're called for the same type that the
// attribute was Set() with.
// AttributeMaps can be nested because AttributeMap is a valid attribute type.
class AttributeMap final {
public:
AttributeMap();
AttributeMap(const AttributeMap& aOther);
AttributeMap& operator=(const AttributeMap& aOther);
bool operator==(const AttributeMap& aOther) const;
bool operator!=(const AttributeMap& aOther) const
{
return !(*this == aOther);
}
~AttributeMap();
void Set(AttributeName aName, bool aValue);
void Set(AttributeName aName, uint32_t aValue);
void Set(AttributeName aName, float aValue);
void Set(AttributeName aName, const Size& aValue);
void Set(AttributeName aName, const IntSize& aValue);
void Set(AttributeName aName, const IntPoint& aValue);
void Set(AttributeName aName, const Matrix& aValue);
void Set(AttributeName aName, const Matrix5x4& aValue);
void Set(AttributeName aName, const Point3D& aValue);
void Set(AttributeName aName, const Color& aValue);
void Set(AttributeName aName, const AttributeMap& aValue);
void Set(AttributeName aName, const float* aValues, int32_t aLength);
bool GetBool(AttributeName aName) const;
uint32_t GetUint(AttributeName aName) const;
float GetFloat(AttributeName aName) const;
Size GetSize(AttributeName aName) const;
IntSize GetIntSize(AttributeName aName) const;
IntPoint GetIntPoint(AttributeName aName) const;
Matrix GetMatrix(AttributeName aName) const;
Matrix5x4 GetMatrix5x4(AttributeName aName) const;
Point3D GetPoint3D(AttributeName aName) const;
Color GetColor(AttributeName aName) const;
AttributeMap GetAttributeMap(AttributeName aName) const;
const nsTArray<float>& GetFloats(AttributeName aName) const;
uint32_t Count() const;
nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator ConstIter() const;
static AttributeType GetType(FilterAttribute* aAttribute);
private:
mutable nsClassHashtable<nsUint32HashKey, FilterAttribute> mMap;
};
enum class ColorSpace {
SRGB,
LinearRGB,
Max
};
enum class AlphaModel {
Unpremultiplied,
Premultiplied
};
class ColorModel {
public:
static ColorModel PremulSRGB()
{
return ColorModel(ColorSpace::SRGB, AlphaModel::Premultiplied);
}
ColorModel(ColorSpace aColorSpace, AlphaModel aAlphaModel) :
mColorSpace(aColorSpace), mAlphaModel(aAlphaModel) {}
ColorModel() :
mColorSpace(ColorSpace::SRGB), mAlphaModel(AlphaModel::Premultiplied) {}
bool operator==(const ColorModel& aOther) const {
return mColorSpace == aOther.mColorSpace &&
mAlphaModel == aOther.mAlphaModel;
}
// Used to index FilterCachedColorModels::mFilterForColorModel.
uint8_t ToIndex() const
{
return (uint8_t(mColorSpace) << 1) + uint8_t(mAlphaModel);
}
ColorSpace mColorSpace;
AlphaModel mAlphaModel;
};
enum class PrimitiveType {
Empty = 0,
Blend,
Morphology,
ColorMatrix,
Flood,
Tile,
ComponentTransfer,
ConvolveMatrix,
Offset,
DisplacementMap,
Turbulence,
Composite,
Merge,
Image,
GaussianBlur,
DropShadow,
DiffuseLighting,
SpecularLighting,
ToAlpha,
Max
};
/**
* A data structure to carry attributes for a given primitive that's part of a
* filter. Will be serializable via IPDL, so it must not contain complex
* functionality.
* Used as part of a FilterDescription.
*/
class FilterPrimitiveDescription final {
public:
enum {
kPrimitiveIndexSourceGraphic = -1,
kPrimitiveIndexSourceAlpha = -2,
kPrimitiveIndexFillPaint = -3,
kPrimitiveIndexStrokePaint = -4
};
FilterPrimitiveDescription();
explicit FilterPrimitiveDescription(PrimitiveType aType);
FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther);
FilterPrimitiveDescription& operator=(const FilterPrimitiveDescription& aOther);
PrimitiveType Type() const { return mType; }
void SetType(PrimitiveType aType) { mType = aType; }
const AttributeMap& Attributes() const { return mAttributes; }
AttributeMap& Attributes() { return mAttributes; }
IntRect PrimitiveSubregion() const { return mFilterPrimitiveSubregion; }
IntRect FilterSpaceBounds() const { return mFilterSpaceBounds; }
bool IsTainted() const { return mIsTainted; }
size_t NumberOfInputs() const { return mInputPrimitives.Length(); }
int32_t InputPrimitiveIndex(size_t aInputIndex) const
{
return aInputIndex < mInputPrimitives.Length() ?
mInputPrimitives[aInputIndex] : 0;
}
ColorSpace InputColorSpace(size_t aInputIndex) const
{
return aInputIndex < mInputColorSpaces.Length() ?
mInputColorSpaces[aInputIndex] : ColorSpace();
}
ColorSpace OutputColorSpace() const { return mOutputColorSpace; }
void SetPrimitiveSubregion(const IntRect& aRect)
{
mFilterPrimitiveSubregion = aRect;
}
void SetFilterSpaceBounds(const IntRect& aRect)
{
mFilterSpaceBounds = aRect;
}
void SetIsTainted(bool aIsTainted)
{
mIsTainted = aIsTainted;
}
void SetInputPrimitive(size_t aInputIndex, int32_t aInputPrimitiveIndex)
{
mInputPrimitives.EnsureLengthAtLeast(aInputIndex + 1);
mInputPrimitives[aInputIndex] = aInputPrimitiveIndex;
}
void SetInputColorSpace(size_t aInputIndex, ColorSpace aColorSpace)
{
mInputColorSpaces.EnsureLengthAtLeast(aInputIndex + 1);
mInputColorSpaces[aInputIndex] = aColorSpace;
}
void SetOutputColorSpace(const ColorSpace& aColorSpace)
{
mOutputColorSpace = aColorSpace;
}
bool operator==(const FilterPrimitiveDescription& aOther) const;
bool operator!=(const FilterPrimitiveDescription& aOther) const
{
return !(*this == aOther);
}
private:
PrimitiveType mType;
AttributeMap mAttributes;
nsTArray<int32_t> mInputPrimitives;
IntRect mFilterPrimitiveSubregion;
IntRect mFilterSpaceBounds;
nsTArray<ColorSpace> mInputColorSpaces;
ColorSpace mOutputColorSpace;
bool mIsTainted;
};
/**
* A data structure that contains one or more FilterPrimitiveDescriptions.
* Designed to be serializable via IPDL, so it must not contain complex
* functionality.
*/
struct FilterDescription final {
FilterDescription() {}
explicit FilterDescription(const nsTArray<FilterPrimitiveDescription>& aPrimitives)
: mPrimitives(aPrimitives)
{}
bool operator==(const FilterDescription& aOther) const;
bool operator!=(const FilterDescription& aOther) const
{
return !(*this == aOther);
}
nsTArray<FilterPrimitiveDescription> mPrimitives;
};
/**
* The methods of this class are not on FilterDescription because
* FilterDescription is designed as a simple value holder that can be used
* on any thread.
*/
class FilterSupport {
public:
/**
* Draw the filter described by aFilter. All rect parameters are in filter
* space coordinates. aRenderRect specifies the part of the filter output
* that will be drawn at (0, 0) into the draw target aDT, subject to the
* current transform on aDT but with no additional scaling.
* The source surfaces must match their corresponding rect in size.
* aAdditionalImages carries the images that are referenced by the
* eImageInputIndex attribute on any image primitives in the filter.
*/
static void
RenderFilterDescription(DrawTarget* aDT,
const FilterDescription& aFilter,
const Rect& aRenderRect,
SourceSurface* aSourceGraphic,
const IntRect& aSourceGraphicRect,
SourceSurface* aFillPaint,
const IntRect& aFillPaintRect,
SourceSurface* aStrokePaint,
const IntRect& aStrokePaintRect,
nsTArray<RefPtr<SourceSurface>>& aAdditionalImages,
const Point& aDestPoint,
const DrawOptions& aOptions = DrawOptions());
/**
* Computes the region that changes in the filter output due to a change in
* input. This is primarily needed when an individual piece of content inside
* a filtered container element changes.
*/
static nsIntRegion
ComputeResultChangeRegion(const FilterDescription& aFilter,
const nsIntRegion& aSourceGraphicChange,
const nsIntRegion& aFillPaintChange,
const nsIntRegion& aStrokePaintChange);
/**
* Computes the regions that need to be supplied in the filter inputs when
* painting aResultNeededRegion of the filter output.
*/
static void
ComputeSourceNeededRegions(const FilterDescription& aFilter,
const nsIntRegion& aResultNeededRegion,
nsIntRegion& aSourceGraphicNeededRegion,
nsIntRegion& aFillPaintNeededRegion,
nsIntRegion& aStrokePaintNeededRegion);
/**
* Computes the size of the filter output.
*/
static nsIntRegion
ComputePostFilterExtents(const FilterDescription& aFilter,
const nsIntRegion& aSourceGraphicExtents);
/**
* Computes the size of a single FilterPrimitiveDescription's output given a
* set of input extents.
*/
static nsIntRegion
PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
const nsTArray<nsIntRegion>& aInputExtents);
};
} // namespace gfx
} // namespace mozilla
#endif // __FilterSupport_h

63
gfx/src/PingPongRegion.h Normal file
View file

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef PingPongRegion_h__
#define PingPongRegion_h__
/* This class uses a pair of regions and swaps between them while
* accumulating to avoid the heap allocations associated with
* modifying a region in place.
*
* It is sizeof(T)*2 + sizeof(T*) and can use end up using
* approximately double the amount of memory as using single
* region so use it sparingly.
*/
template <typename T>
class PingPongRegion
{
typedef typename T::RectType RectType;
public:
PingPongRegion()
{
rgn = &rgn1;
}
void SubOut(const RectType& aOther)
{
T* nextRgn = nextRegion();
nextRgn->Sub(*rgn, aOther);
rgn = nextRgn;
}
void OrWith(const RectType& aOther)
{
T* nextRgn = nextRegion();
nextRgn->Or(*rgn, aOther);
rgn = nextRgn;
}
T& Region()
{
return *rgn;
}
private:
T* nextRegion()
{
if (rgn == &rgn1) {
return &rgn2;
} else {
return &rgn1;
}
}
T* rgn;
T rgn1;
T rgn2;
};
#endif

32
gfx/src/RegionBuilder.h Normal file
View file

@ -0,0 +1,32 @@
/* 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/. */
#ifndef RegionBuilder_h__
#define RegionBuilder_h__
#include <nsTArray.h>
template <typename RegionType>
class RegionBuilder
{
public:
typedef typename RegionType::RectType RectType;
RegionBuilder()
{}
void OrWith(const RectType& aRect) {
pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
mRects.AppendElement(box);
}
RegionType ToRegion() const {
return RegionType(mRects);
}
private:
nsTArray<pixman_box32_t> mRects;
};
#endif // RegionBuilder_h__

370
gfx/src/TiledRegion.cpp Normal file
View file

@ -0,0 +1,370 @@
/* -*- 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 "TiledRegion.h"
#include <algorithm>
#include "mozilla/fallible.h"
namespace mozilla {
namespace gfx {
static const int32_t kTileSize = 256;
static const size_t kMaxTiles = 1000;
/**
* TiledRegionImpl stores an array of non-empty rectangles (pixman_box32_ts) to
* represent the region. Each rectangle is contained in a single tile;
* rectangles never cross tile boundaries. The rectangles are sorted by their
* tile's origin in top-to-bottom, left-to-right order.
* (Note that this can mean that a rectangle r1 can come before another
* rectangle r2 even if r2.y1 < r1.y1, as long as the two rects are in the same
* row of tiles and r1.x1 < r2.x1.)
* Empty tiles take up no space in the array - there is no rectangle stored for
* them. As a result, any algorithm that needs to deal with empty tiles will
* iterate through the mRects array and compare the positions of two
* consecutive rects to figure out whether there are any empty tiles between
* them.
*/
static pixman_box32_t
IntersectionOfNonEmptyBoxes(const pixman_box32_t& aBox1,
const pixman_box32_t& aBox2)
{
return pixman_box32_t {
std::max(aBox1.x1, aBox2.x1),
std::max(aBox1.y1, aBox2.y1),
std::min(aBox1.x2, aBox2.x2),
std::min(aBox1.y2, aBox2.y2)
};
}
// A TileIterator points to a specific tile inside a certain tile range, or to
// the end of the tile range. Advancing a TileIterator will move to the next
// tile inside the range (or to the range end). The next tile is either the
// tile to the right of the current one, or the first tile of the next tile
// row if the current tile is already the last tile in the row.
class TileIterator {
public:
TileIterator(const pixman_box32_t& aTileBounds, const IntPoint& aPosition)
: mTileBounds(aTileBounds)
, mPos(aPosition)
{}
bool operator!=(const TileIterator& aOther) { return mPos != aOther.mPos; }
bool operator==(const TileIterator& aOther) { return mPos == aOther.mPos; }
IntPoint operator*() const { return mPos; }
const TileIterator& operator++() {
mPos.x += kTileSize;
if (mPos.x >= mTileBounds.x2) {
mPos.x = mTileBounds.x1;
mPos.y += kTileSize;
}
return *this;
}
TileIterator& operator=(const IntPoint& aPosition)
{
mPos = aPosition;
return *this;
}
bool IsBeforeTileContainingPoint(const IntPoint& aPoint) const
{
return (mPos.y + kTileSize) <= aPoint.y ||
(mPos.y <= aPoint.y && (mPos.x + kTileSize) <= aPoint.x);
}
bool IsAtTileContainingPoint(const IntPoint& aPoint) const
{
return mPos.y <= aPoint.y && aPoint.y < (mPos.y + kTileSize) &&
mPos.x <= aPoint.x && aPoint.x < (mPos.x + kTileSize);
}
pixman_box32_t IntersectionWith(const pixman_box32_t& aRect) const
{
pixman_box32_t tile = { mPos.x, mPos.y,
mPos.x + kTileSize, mPos.y + kTileSize };
return IntersectionOfNonEmptyBoxes(tile, aRect);
}
private:
const pixman_box32_t& mTileBounds;
IntPoint mPos;
};
// A TileRange describes a range of tiles contained inside a certain tile
// bounds (which is a rectangle that includes all tiles that you're
// interested in). The tile range can start and end at any point inside a
// tile row.
// The tile range end is described by the tile that starts at the bottom
// left corner of the tile bounds, i.e. the first tile under the tile
// bounds.
class TileRange {
public:
// aTileBounds, aStart and aEnd need to be aligned with the tile grid.
TileRange(const pixman_box32_t& aTileBounds,
const IntPoint& aStart, const IntPoint& aEnd)
: mTileBounds(aTileBounds)
, mStart(aStart)
, mEnd(aEnd)
{}
// aTileBounds needs to be aligned with the tile grid.
explicit TileRange(const pixman_box32_t& aTileBounds)
: mTileBounds(aTileBounds)
, mStart(mTileBounds.x1, mTileBounds.y1)
, mEnd(mTileBounds.x1, mTileBounds.y2)
{}
TileIterator Begin() const { return TileIterator(mTileBounds, mStart); }
TileIterator End() const { return TileIterator(mTileBounds, mEnd); }
// The number of tiles in this tile range.
size_t Length() const
{
if (mEnd.y == mStart.y) {
return (mEnd.x - mStart.x) / kTileSize;
}
int64_t numberOfFullRows = (((int64_t)mEnd.y - (int64_t)mStart.y) / kTileSize) - 1;
int64_t tilesInFirstRow = ((int64_t)mTileBounds.x2 - (int64_t)mStart.x) / kTileSize;
int64_t tilesInLastRow = ((int64_t)mEnd.x - (int64_t)mTileBounds.x1) / kTileSize;
int64_t tilesInFullRow = ((int64_t)mTileBounds.x2 - (int64_t)mTileBounds.x1) / kTileSize;
int64_t total = tilesInFirstRow + (tilesInFullRow * numberOfFullRows) + tilesInLastRow;
MOZ_ASSERT(total > 0);
// The total may be larger than what fits in a size_t, so clamp it to
// SIZE_MAX in that case.
return ((uint64_t)total > (uint64_t)SIZE_MAX) ? SIZE_MAX : (size_t)total;
}
// If aTileOrigin does not describe a tile inside our tile bounds, move it
// to the next tile that you'd encounter by "advancing" a tile iterator
// inside these tile bounds. If aTileOrigin is after the last tile inside
// our tile bounds, move it to the range end tile.
// The result of this method is a valid end tile for a tile range with our
// tile bounds.
IntPoint MoveIntoBounds(const IntPoint& aTileOrigin) const
{
IntPoint p = aTileOrigin;
if (p.x < mTileBounds.x1) {
p.x = mTileBounds.x1;
} else if (p.x >= mTileBounds.x2) {
p.x = mTileBounds.x1;
p.y += kTileSize;
}
if (p.y < mTileBounds.y1) {
p.y = mTileBounds.y1;
p.x = mTileBounds.x1;
} else if (p.y >= mTileBounds.y2) {
// There's only one valid state after the end of the tile range, and that's
// the bottom left point of the tile bounds.
p.x = mTileBounds.x1;
p.y = mTileBounds.y2;
}
return p;
}
private:
const pixman_box32_t& mTileBounds;
const IntPoint mStart;
const IntPoint mEnd;
};
static IntPoint
TileContainingPoint(const IntPoint& aPoint)
{
return IntPoint(RoundDownToMultiple(aPoint.x, kTileSize),
RoundDownToMultiple(aPoint.y, kTileSize));
}
enum class IterationAction : uint8_t {
CONTINUE,
STOP
};
enum class IterationEndReason : uint8_t {
NOT_STOPPED,
STOPPED
};
template<
typename HandleEmptyTilesFunction,
typename HandleNonEmptyTileFunction,
typename RectArrayT>
IterationEndReason ProcessIntersectedTiles(const pixman_box32_t& aRect,
RectArrayT& aRectArray,
HandleEmptyTilesFunction aHandleEmptyTiles,
HandleNonEmptyTileFunction aHandleNonEmptyTile)
{
pixman_box32_t tileBounds = {
RoundDownToMultiple(aRect.x1, kTileSize),
RoundDownToMultiple(aRect.y1, kTileSize),
RoundUpToMultiple(aRect.x2, kTileSize),
RoundUpToMultiple(aRect.y2, kTileSize)
};
if (tileBounds.x2 < tileBounds.x1 || tileBounds.y2 < tileBounds.y1) {
// RoundUpToMultiple probably overflowed. Bail out.
return IterationEndReason::STOPPED;
}
TileRange tileRange(tileBounds);
TileIterator rangeEnd = tileRange.End();
// tileIterator points to the next tile in tileRange, or to rangeEnd if we're
// done.
TileIterator tileIterator = tileRange.Begin();
// We iterate over the rectangle array. Depending on the position of the
// rectangle we encounter, we may need to advance tileIterator by zero, one,
// or more tiles:
// - Zero if the rectangle we encountered is outside the tiles that
// intersect aRect.
// - One if the rectangle is in the exact tile that we're interested in next
// (i.e. the tile that tileIterator points at).
// - More than one if the encountered rectangle is in a tile that's further
// to the right or to the bottom than tileIterator. In that case there is
// at least one empty tile between the last rectangle we encountered and
// the current one.
for (size_t i = 0; i < aRectArray.Length() && tileIterator != rangeEnd; i++) {
MOZ_ASSERT(aRectArray[i].x1 < aRectArray[i].x2 && aRectArray[i].y1 < aRectArray[i].y2, "empty rect");
IntPoint rectOrigin(aRectArray[i].x1, aRectArray[i].y1);
if (tileIterator.IsBeforeTileContainingPoint(rectOrigin)) {
IntPoint tileOrigin = TileContainingPoint(rectOrigin);
IntPoint afterEmptyTiles = tileRange.MoveIntoBounds(tileOrigin);
TileRange emptyTiles(tileBounds, *tileIterator, afterEmptyTiles);
if (aHandleEmptyTiles(aRectArray, i, emptyTiles) == IterationAction::STOP) {
return IterationEndReason::STOPPED;
}
tileIterator = afterEmptyTiles;
if (tileIterator == rangeEnd) {
return IterationEndReason::NOT_STOPPED;
}
}
if (tileIterator.IsAtTileContainingPoint(rectOrigin)) {
pixman_box32_t rectIntersection = tileIterator.IntersectionWith(aRect);
if (aHandleNonEmptyTile(aRectArray, i, rectIntersection) == IterationAction::STOP) {
return IterationEndReason::STOPPED;
}
++tileIterator;
}
}
if (tileIterator != rangeEnd) {
// We've looked at all of our existing rectangles but haven't covered all
// of the tiles that we're interested in yet. So we need to deal with the
// remaining tiles now.
size_t endIndex = aRectArray.Length();
TileRange emptyTiles(tileBounds, *tileIterator, *rangeEnd);
if (aHandleEmptyTiles(aRectArray, endIndex, emptyTiles) == IterationAction::STOP) {
return IterationEndReason::STOPPED;
}
}
return IterationEndReason::NOT_STOPPED;
}
static pixman_box32_t
UnionBoundsOfNonEmptyBoxes(const pixman_box32_t& aBox1,
const pixman_box32_t& aBox2)
{
return { std::min(aBox1.x1, aBox2.x1),
std::min(aBox1.y1, aBox2.y1),
std::max(aBox1.x2, aBox2.x2),
std::max(aBox1.y2, aBox2.y2) };
}
// Returns true when adding the rectangle was successful, and false if
// allocation failed.
// When this returns false, our internal state might not be consistent and we
// need to be cleared.
bool
TiledRegionImpl::AddRect(const pixman_box32_t& aRect)
{
// We are adding a rectangle that can span multiple tiles.
// For each empty tile that aRect intersects, we need to add the intersection
// of aRect with that tile to mRects, respecting the order of mRects.
// For each tile that already has a rectangle, we need to enlarge that
// existing rectangle to include the intersection of aRect with the tile.
return ProcessIntersectedTiles(aRect, mRects,
[&aRect](nsTArray<pixman_box32_t>& rects, size_t& rectIndex, TileRange emptyTiles) {
CheckedInt<size_t> newLength(rects.Length());
newLength += emptyTiles.Length();
if (!newLength.isValid() || newLength.value() >= kMaxTiles ||
!rects.InsertElementsAt(rectIndex, emptyTiles.Length(), fallible)) {
return IterationAction::STOP;
}
for (TileIterator tileIt = emptyTiles.Begin();
tileIt != emptyTiles.End();
++tileIt, ++rectIndex) {
rects[rectIndex] = tileIt.IntersectionWith(aRect);
}
return IterationAction::CONTINUE;
},
[](nsTArray<pixman_box32_t>& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) {
rects[rectIndex] =
UnionBoundsOfNonEmptyBoxes(rects[rectIndex], rectIntersectionWithTile);
return IterationAction::CONTINUE;
}) == IterationEndReason::NOT_STOPPED;
}
static bool
NonEmptyBoxesIntersect(const pixman_box32_t& aBox1, const pixman_box32_t& aBox2)
{
return aBox1.x1 < aBox2.x2 && aBox2.x1 < aBox1.x2 &&
aBox1.y1 < aBox2.y2 && aBox2.y1 < aBox1.y2;
}
bool
TiledRegionImpl::Intersects(const pixman_box32_t& aRect) const
{
// aRect intersects this region if it intersects any of our rectangles.
return ProcessIntersectedTiles(aRect, mRects,
[](const nsTArray<pixman_box32_t>& rects, size_t& rectIndex, TileRange emptyTiles) {
// Ignore empty tiles and keep on iterating.
return IterationAction::CONTINUE;
},
[](const nsTArray<pixman_box32_t>& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) {
if (NonEmptyBoxesIntersect(rects[rectIndex], rectIntersectionWithTile)) {
// Found an intersecting rectangle, so aRect intersects this region.
return IterationAction::STOP;
}
return IterationAction::CONTINUE;
}) == IterationEndReason::STOPPED;
}
static bool
NonEmptyBoxContainsNonEmptyBox(const pixman_box32_t& aBox1, const pixman_box32_t& aBox2)
{
return aBox1.x1 <= aBox2.x1 && aBox2.x2 <= aBox1.x2 &&
aBox1.y1 <= aBox2.y1 && aBox2.y2 <= aBox1.y2;
}
bool
TiledRegionImpl::Contains(const pixman_box32_t& aRect) const
{
// aRect is contained in this region if aRect does not intersect any empty
// tiles and, for each non-empty tile, if the intersection of aRect with that
// tile is contained in the existing rectangle we have in that tile.
return ProcessIntersectedTiles(aRect, mRects,
[](const nsTArray<pixman_box32_t>& rects, size_t& rectIndex, TileRange emptyTiles) {
// Found an empty tile that intersects aRect, so aRect is not contained
// in this region.
return IterationAction::STOP;
},
[](const nsTArray<pixman_box32_t>& rects, size_t rectIndex, const pixman_box32_t& rectIntersectionWithTile) {
if (!NonEmptyBoxContainsNonEmptyBox(rects[rectIndex], rectIntersectionWithTile)) {
// Our existing rectangle in this tile does not cover the part of aRect that
// intersects this tile, so aRect is not contained in this region.
return IterationAction::STOP;
}
return IterationAction::CONTINUE;
}) == IterationEndReason::NOT_STOPPED;
}
} // namespace gfx
} // namespace mozilla

208
gfx/src/TiledRegion.h Normal file
View file

@ -0,0 +1,208 @@
/* -*- 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/. */
#ifndef MOZILLA_GFX_TILEDREGION_H_
#define MOZILLA_GFX_TILEDREGION_H_
#include "mozilla/ArrayView.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/Move.h"
#include "nsRegion.h"
#include "pixman.h"
namespace mozilla {
namespace gfx {
// See TiledRegion.cpp for documentation on TiledRegionImpl.
class TiledRegionImpl {
public:
void Clear() { mRects.Clear(); }
bool AddRect(const pixman_box32_t& aRect);
bool Intersects(const pixman_box32_t& aRect) const;
bool Contains(const pixman_box32_t& aRect) const;
operator ArrayView<pixman_box32_t>() const { return ArrayView<pixman_box32_t>(mRects); }
private:
nsTArray<pixman_box32_t> mRects;
};
/**
* A auto-simplifying region type that supports one rectangle per tile.
* The virtual tile grid is anchored at (0, 0) and has quadratic tiles whose
* size is hard-coded as kTileSize in TiledRegion.cpp.
* A TiledRegion starts out empty. You can add rectangles or (regular) regions
* into it by calling Add(). Add() is a mutating union operation (similar to
* OrWith on nsRegion) that's *not* exact, because it will enlarge the region as
* necessary to satisfy the "one rectangle per tile" requirement.
* Tiled regions convert implicitly to the underlying regular region type.
* The only way to remove parts from a TiledRegion is by calling SetEmpty().
*/
template<typename RegionT>
class TiledRegion {
public:
typedef typename RegionT::RectType RectT;
TiledRegion()
: mCoversBounds(false)
{}
TiledRegion(const TiledRegion& aOther)
: mBounds(aOther.mBounds)
, mImpl(aOther.mImpl)
, mCoversBounds(false)
{}
TiledRegion(TiledRegion&& aOther)
: mBounds(aOther.mBounds)
, mImpl(Move(aOther.mImpl))
, mCoversBounds(false)
{}
RegionT GetRegion() const
{
if (mBounds.IsEmpty()) {
return RegionT();
}
if (mCoversBounds) {
// Rect limit hit or allocation failed, treat as 1 rect.
return RegionT(mBounds);
}
return RegionT(mImpl);
}
TiledRegion& operator=(const TiledRegion& aOther)
{
if (&aOther != this) {
mBounds = aOther.mBounds;
mImpl = aOther.mImpl;
mCoversBounds = aOther.mCoversBounds;
}
return *this;
}
void Add(const RectT& aRect)
{
if (aRect.IsEmpty()) {
return;
}
Maybe<RectT> newBounds = mBounds.SafeUnion(aRect);
if (!newBounds) {
return;
}
mBounds = newBounds.value();
MOZ_ASSERT(!mBounds.Overflows());
if (mCoversBounds) {
return;
}
if (!mImpl.AddRect(RectToBox(aRect))) {
FallBackToBounds();
}
}
void Add(const RegionT& aRegion)
{
Maybe<RectT> newBounds = mBounds.SafeUnion(aRegion.GetBounds());
if (!newBounds) {
return;
}
mBounds = newBounds.value();
MOZ_ASSERT(!mBounds.Overflows());
if (mCoversBounds) {
return;
}
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
RectT r = iter.Get();
if (r.IsEmpty() || r.Overflows()) {
// This can happen if e.g. a negative-width rect was wrapped into a
// region. Treat it the same as we would if such a rect was passed to
// the Add(const RectT&) function.
continue;
}
if (!mImpl.AddRect(RectToBox(r))) {
FallBackToBounds();
return;
}
}
}
bool IsEmpty() const { return mBounds.IsEmpty(); }
void SetEmpty()
{
mBounds.SetEmpty();
mImpl.Clear();
mCoversBounds = false;
}
RectT GetBounds() const { return mBounds; }
bool CoversBounds() const { return mCoversBounds; }
bool Intersects(const RectT& aRect) const
{
if (aRect.IsEmpty()) {
return true;
}
if (aRect.Overflows() || !mBounds.Intersects(aRect)) {
return false;
}
if (mCoversBounds) {
return true;
}
return mImpl.Intersects(RectToBox(aRect));
}
bool Contains(const RectT& aRect) const
{
if (aRect.IsEmpty()) {
return true;
}
if (aRect.Overflows() || !mBounds.Contains(aRect)) {
return false;
}
if (mCoversBounds) {
return true;
}
return mImpl.Contains(RectToBox(aRect));
}
private:
void FallBackToBounds()
{
mCoversBounds = true;
mImpl.Clear();
}
static pixman_box32_t RectToBox(const RectT& aRect)
{
MOZ_ASSERT(!aRect.IsEmpty());
MOZ_ASSERT(!aRect.Overflows());
return { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
}
RectT mBounds;
TiledRegionImpl mImpl;
// mCoversBounds is true if we bailed out due to a large number of tiles.
// mCoversBounds being true means that this TiledRegion is just a simple
// rectangle (our mBounds).
// Once set to true, the TiledRegion will stay in this state until SetEmpty
// is called.
bool mCoversBounds;
};
typedef TiledRegion<IntRegion> TiledIntRegion;
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_TILEDREGION_H_ */

23
gfx/src/X11UndefineNone.h Normal file
View file

@ -0,0 +1,23 @@
/* -*- 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/. */
// The header <X11/X.h> defines "None" as a macro that expands to "0L".
// This is terrible because many enumerations have an enumerator named "None".
// To work around this, we undefine the macro "None", and define a replacement
// macro named "X11None".
// Include this header after including X11 headers, where necessary.
#ifdef None
# undef None
# define X11None 0L
// <X11/X.h> also defines "RevertToNone" as a macro that expands to "(int)None".
// Since we are undefining "None", that stops working. To keep it working,
// we undefine "RevertToNone" and redefine it in terms of "X11None".
# ifdef RevertToNone
# undef RevertToNone
# define RevertToNone (int)X11None
# endif
#endif

94
gfx/src/X11Util.cpp Normal file
View file

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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 "X11Util.h"
#include "nsDebug.h" // for NS_ASSERTION, etc
#include "MainThreadUtils.h" // for NS_IsMainThread
namespace mozilla {
void
FindVisualAndDepth(Display* aDisplay, VisualID aVisualID,
Visual** aVisual, int* aDepth)
{
const Screen* screen = DefaultScreenOfDisplay(aDisplay);
for (int d = 0; d < screen->ndepths; d++) {
Depth *d_info = &screen->depths[d];
for (int v = 0; v < d_info->nvisuals; v++) {
Visual* visual = &d_info->visuals[v];
if (visual->visualid == aVisualID) {
*aVisual = visual;
*aDepth = d_info->depth;
return;
}
}
}
NS_ASSERTION(aVisualID == X11None, "VisualID not on Screen.");
*aVisual = nullptr;
*aDepth = 0;
return;
}
void
FinishX(Display* aDisplay)
{
unsigned long lastRequest = NextRequest(aDisplay) - 1;
if (lastRequest == LastKnownRequestProcessed(aDisplay))
return;
XSync(aDisplay, False);
}
ScopedXErrorHandler::ErrorEvent* ScopedXErrorHandler::sXErrorPtr;
int
ScopedXErrorHandler::ErrorHandler(Display *, XErrorEvent *ev)
{
// only record the error if no error was previously recorded.
// this means that in case of multiple errors, it's the first error that we report.
if (!sXErrorPtr->mError.error_code)
sXErrorPtr->mError = *ev;
return 0;
}
ScopedXErrorHandler::ScopedXErrorHandler(bool aAllowOffMainThread)
{
if (!aAllowOffMainThread) {
// Off main thread usage is not safe in general, but OMTC GL layers uses this
// with the main thread blocked, which makes it safe.
NS_WARNING_ASSERTION(
NS_IsMainThread(),
"ScopedXErrorHandler being called off main thread, may cause issues");
}
// let sXErrorPtr point to this object's mXError object, but don't reset this mXError object!
// think of the case of nested ScopedXErrorHandler's.
mOldXErrorPtr = sXErrorPtr;
sXErrorPtr = &mXError;
mOldErrorHandler = XSetErrorHandler(ErrorHandler);
}
ScopedXErrorHandler::~ScopedXErrorHandler()
{
sXErrorPtr = mOldXErrorPtr;
XSetErrorHandler(mOldErrorHandler);
}
bool
ScopedXErrorHandler::SyncAndGetError(Display *dpy, XErrorEvent *ev)
{
FinishX(dpy);
bool retval = mXError.mError.error_code != 0;
if (ev)
*ev = mXError.mError;
mXError = ErrorEvent(); // reset
return retval;
}
} // namespace mozilla

148
gfx/src/X11Util.h Normal file
View file

@ -0,0 +1,148 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#ifndef mozilla_X11Util_h
#define mozilla_X11Util_h
// Utilities common to all X clients, regardless of UI toolkit.
#if defined(MOZ_WIDGET_GTK)
# include <gdk/gdk.h>
# include <gdk/gdkx.h>
# include "X11UndefineNone.h"
#else
# error Unknown toolkit
#endif
#include <string.h> // for memset
#include "mozilla/Scoped.h" // for SCOPED_TEMPLATE
namespace mozilla {
/**
* Return the default X Display created and used by the UI toolkit.
*/
inline Display*
DefaultXDisplay()
{
#if defined(MOZ_WIDGET_GTK)
return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
#endif
}
/**
* Sets *aVisual to point to aDisplay's Visual struct corresponding to
* aVisualID, and *aDepth to its depth. When aVisualID is None, these are set
* to nullptr and 0 respectively. Both out-parameter pointers are assumed
* non-nullptr.
*/
void
FindVisualAndDepth(Display* aDisplay, VisualID aVisualID,
Visual** aVisual, int* aDepth);
/**
* Ensure that all X requests have been processed.
*
* This is similar to XSync, but doesn't need a round trip if the previous
* request was synchronous or if events have been received since the last
* request. Subsequent FinishX calls will be noops if there have been no
* intermediate requests.
*/
void
FinishX(Display* aDisplay);
/**
* Invoke XFree() on a pointer to memory allocated by Xlib (if the
* pointer is nonnull) when this class goes out of scope.
*/
template <typename T>
struct ScopedXFreePtrTraits
{
typedef T *type;
static T *empty() { return nullptr; }
static void release(T *ptr) { if (ptr != nullptr) XFree(ptr); }
};
SCOPED_TEMPLATE(ScopedXFree, ScopedXFreePtrTraits)
/**
* On construction, set a graceful X error handler that doesn't crash the application and records X errors.
* On destruction, restore the X error handler to what it was before construction.
*
* The SyncAndGetError() method allows to know whether a X error occurred, optionally allows to get the full XErrorEvent,
* and resets the recorded X error state so that a single X error will be reported only once.
*
* Nesting is correctly handled: multiple nested ScopedXErrorHandler's don't interfere with each other's state. However,
* if SyncAndGetError is not called on the nested ScopedXErrorHandler, then any X errors caused by X calls made while the nested
* ScopedXErrorHandler was in place may then be caught by the other ScopedXErrorHandler. This is just a result of X being
* asynchronous and us not doing any implicit syncing: the only method in this class what causes syncing is SyncAndGetError().
*
* This class is not thread-safe at all. It is assumed that only one thread is using any ScopedXErrorHandler's. Given that it's
* not used on Mac, it should be easy to make it thread-safe by using thread-local storage with __thread.
*/
class ScopedXErrorHandler
{
public:
// trivial wrapper around XErrorEvent, just adding ctor initializing by zero.
struct ErrorEvent
{
XErrorEvent mError;
ErrorEvent()
{
memset(this, 0, sizeof(ErrorEvent));
}
};
private:
// this ScopedXErrorHandler's ErrorEvent object
ErrorEvent mXError;
// static pointer for use by the error handler
static ErrorEvent* sXErrorPtr;
// what to restore sXErrorPtr to on destruction
ErrorEvent* mOldXErrorPtr;
// what to restore the error handler to on destruction
int (*mOldErrorHandler)(Display *, XErrorEvent *);
public:
static int
ErrorHandler(Display *, XErrorEvent *ev);
/**
* @param aAllowOffMainThread whether to warn if used off main thread
*/
explicit ScopedXErrorHandler(bool aAllowOffMainThread = false);
~ScopedXErrorHandler();
/** \returns true if a X error occurred since the last time this method was called on this ScopedXErrorHandler object,
* or since the creation of this ScopedXErrorHandler object if this method was never called on it.
*
* \param ev this optional parameter, if set, will be filled with the XErrorEvent object. If multiple errors occurred,
* the first one will be returned.
*/
bool SyncAndGetError(Display *dpy, XErrorEvent *ev = nullptr);
};
class OffMainThreadScopedXErrorHandler : public ScopedXErrorHandler
{
public:
OffMainThreadScopedXErrorHandler()
: ScopedXErrorHandler(true)
{
}
};
} // namespace mozilla
#endif // mozilla_X11Util_h

View file

@ -0,0 +1,145 @@
/* -*- Mode: C++; tab-width: 2; 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 "gfxCrashReporterUtils.h"
#if defined(MOZ_CRASHREPORTER)
#define MOZ_GFXFEATUREREPORTER 1
#endif
#ifdef MOZ_GFXFEATUREREPORTER
#include "gfxCrashReporterUtils.h"
#include <string.h> // for strcmp
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/Services.h" // for GetObserverService
#include "mozilla/StaticMutex.h"
#include "mozilla/mozalloc.h" // for operator new, etc
#include "mozilla/RefPtr.h" // for RefPtr
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsError.h" // for NS_OK, NS_FAILED, nsresult
#include "nsExceptionHandler.h" // for AppendAppNotesToCrashReport
#include "nsID.h"
#include "nsIEventTarget.h" // for NS_DISPATCH_NORMAL
#include "nsIObserver.h" // for nsIObserver, etc
#include "nsIObserverService.h" // for nsIObserverService
#include "nsIRunnable.h" // for nsIRunnable
#include "nsISupports.h"
#include "nsTArray.h" // for nsTArray
#include "nsThreadUtils.h" // for NS_DispatchToMainThread, etc
#include "nscore.h" // for NS_IMETHOD, NS_IMETHODIMP, etc
namespace mozilla {
static nsTArray<nsCString> *gFeaturesAlreadyReported = nullptr;
static StaticMutex gFeaturesAlreadyReportedMutex;
class ObserverToDestroyFeaturesAlreadyReported final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ObserverToDestroyFeaturesAlreadyReported() {}
private:
virtual ~ObserverToDestroyFeaturesAlreadyReported() {}
};
NS_IMPL_ISUPPORTS(ObserverToDestroyFeaturesAlreadyReported,
nsIObserver)
NS_IMETHODIMP
ObserverToDestroyFeaturesAlreadyReported::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, "xpcom-shutdown")) {
StaticMutexAutoLock al(gFeaturesAlreadyReportedMutex);
if (gFeaturesAlreadyReported) {
delete gFeaturesAlreadyReported;
gFeaturesAlreadyReported = nullptr;
}
}
return NS_OK;
}
class RegisterObserverRunnable : public Runnable {
public:
NS_IMETHOD Run() override {
// LeakLog made me do this. Basically, I just wanted gFeaturesAlreadyReported to be a static nsTArray<nsCString>,
// and LeakLog was complaining about leaks like this:
// leaked 1 instance of nsTArray_base with size 8 bytes
// leaked 7 instances of nsStringBuffer with size 8 bytes each (56 bytes total)
// So this is a work-around using a pointer, and using a nsIObserver to deallocate on xpcom shutdown.
// Yay for fighting bloat.
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (!observerService)
return NS_OK;
RefPtr<ObserverToDestroyFeaturesAlreadyReported> observer = new ObserverToDestroyFeaturesAlreadyReported;
observerService->AddObserver(observer, "xpcom-shutdown", false);
return NS_OK;
}
};
class AppendAppNotesRunnable : public CancelableRunnable {
public:
explicit AppendAppNotesRunnable(const nsACString& aFeatureStr)
: mFeatureString(aFeatureStr)
{
}
NS_IMETHOD Run() override {
CrashReporter::AppendAppNotesToCrashReport(mFeatureString);
return NS_OK;
}
private:
nsAutoCString mFeatureString;
};
void
ScopedGfxFeatureReporter::WriteAppNote(char statusChar)
{
StaticMutexAutoLock al(gFeaturesAlreadyReportedMutex);
if (!gFeaturesAlreadyReported) {
gFeaturesAlreadyReported = new nsTArray<nsCString>;
nsCOMPtr<nsIRunnable> r = new RegisterObserverRunnable();
NS_DispatchToMainThread(r);
}
nsAutoCString featureString;
featureString.AppendPrintf("%s%c ",
mFeature,
statusChar);
if (!gFeaturesAlreadyReported->Contains(featureString)) {
gFeaturesAlreadyReported->AppendElement(featureString);
AppNote(featureString);
}
}
void
ScopedGfxFeatureReporter::AppNote(const nsACString& aMessage)
{
if (NS_IsMainThread()) {
CrashReporter::AppendAppNotesToCrashReport(aMessage);
} else {
nsCOMPtr<nsIRunnable> r = new AppendAppNotesRunnable(aMessage);
NS_DispatchToMainThread(r);
}
}
} // end namespace mozilla
#else
namespace mozilla {
void ScopedGfxFeatureReporter::WriteAppNote(char) {}
void ScopedGfxFeatureReporter::AppNote(const nsACString&) {}
} // namespace mozilla
#endif

View file

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef gfxCrashReporterUtils_h__
#define gfxCrashReporterUtils_h__
#include "nsString.h"
namespace mozilla {
/** \class ScopedGfxFeatureReporter
*
* On creation, adds "FeatureName?" to AppNotes
* On destruction, adds "FeatureName-", or "FeatureName+" if you called SetSuccessful().
*
* Any such string is added at most once to AppNotes, and is subsequently skipped.
*
* This ScopedGfxFeatureReporter class is designed to be fool-proof to use in functions that
* have many exit points. We don't want to encourage having function with many exit points.
* It just happens that our graphics features initialization functions are like that.
*/
class ScopedGfxFeatureReporter
{
public:
explicit ScopedGfxFeatureReporter(const char *aFeature, bool aForce = false)
: mFeature(aFeature), mStatusChar('-')
{
WriteAppNote(aForce ? '!' : '?');
}
~ScopedGfxFeatureReporter() {
WriteAppNote(mStatusChar);
}
void SetSuccessful() { mStatusChar = '+'; }
static void AppNote(const nsACString& aMessage);
class AppNoteWritingRunnable;
protected:
const char *mFeature;
char mStatusChar;
private:
void WriteAppNote(char statusChar);
};
} // end namespace mozilla
#endif // gfxCrashReporterUtils_h__

59
gfx/src/gfxTelemetry.cpp Normal file
View file

@ -0,0 +1,59 @@
/* -*- Mode: IDL; tab-width: 2; 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 "gfxTelemetry.h"
namespace mozilla {
namespace gfx {
const char*
FeatureStatusToString(FeatureStatus aStatus)
{
switch (aStatus) {
case FeatureStatus::Unused:
return "unused";
case FeatureStatus::Unavailable:
return "unavailable";
case FeatureStatus::CrashedInHandler:
return "crashed";
case FeatureStatus::Blocked:
return "blocked";
case FeatureStatus::Blacklisted:
return "blacklisted";
case FeatureStatus::Failed:
return "failed";
case FeatureStatus::Disabled:
return "disabled";
case FeatureStatus::Available:
return "available";
case FeatureStatus::ForceEnabled:
return "force_enabled";
case FeatureStatus::CrashedOnStartup:
return "crashed_on_startup";
case FeatureStatus::Broken:
return "broken";
default:
MOZ_ASSERT_UNREACHABLE("missing status case");
return "unknown";
}
}
bool
IsFeatureStatusFailure(FeatureStatus aStatus)
{
return !(aStatus == FeatureStatus::Unused ||
aStatus == FeatureStatus::Available ||
aStatus == FeatureStatus::ForceEnabled);
}
bool
IsFeatureStatusSuccess(FeatureStatus aStatus)
{
return aStatus == FeatureStatus::Available ||
aStatus == FeatureStatus::ForceEnabled;
}
} // namespace gfx
} // namespace mozilla

70
gfx/src/gfxTelemetry.h Normal file
View file

@ -0,0 +1,70 @@
/* -*- Mode: IDL; tab-width: 2; 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/. */
#ifndef gfx_src_gfxTelemetry_h__
#define gfx_src_gfxTelemetry_h__
namespace mozilla {
namespace gfx {
// Describes the status of a graphics feature, in terms of whether or not we've
// attempted to initialize the feature, and if so, whether or not it succeeded
// (and if not, why).
enum class FeatureStatus
{
// This feature has not been requested.
Unused,
// This feature is unavailable due to Safe Mode, not being included with
// the operating system, or a dependent feature being disabled.
Unavailable,
// This feature crashed immediately when we tried to initialize it, but we
// were able to recover via SEH (or something similar).
CrashedInHandler,
// This feature was blocked for reasons outside the blacklist, such as a
// runtime test failing.
Blocked,
// This feature has been blocked by the graphics blacklist.
Blacklisted,
// This feature was attempted but failed to activate.
Failed,
// This feature was explicitly disabled by the user.
Disabled,
// This feature is available for use.
Available,
// This feature was explicitly force-enabled by the user.
ForceEnabled,
// This feature was disabled due to the startup crash guard.
CrashedOnStartup,
// This feature was attempted but later determined to be broken.
Broken,
// Add new entries above here.
LAST
};
const char* FeatureStatusToString(FeatureStatus aStatus);
bool IsFeatureStatusFailure(FeatureStatus aStatus);
bool IsFeatureStatusSuccess(FeatureStatus aStatus);
enum class TelemetryDeviceCode : uint32_t {
Content = 0,
Image = 1,
D2D1 = 2
};
} // namespace gfx
} // namespace mozilla
#endif // gfx_src_gfxTelemetry_h__

94
gfx/src/moz.build Normal file
View file

@ -0,0 +1,94 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPIDL_SOURCES += [
'nsIFontEnumerator.idl',
'nsIScriptableRegion.idl',
]
XPIDL_MODULE = 'gfx'
DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
EXPORTS += [
'DriverCrashGuard.h',
'FilterSupport.h',
'gfxCrashReporterUtils.h',
'gfxTelemetry.h',
'nsBoundingMetrics.h',
'nsColor.h',
'nsColorNameList.h',
'nsColorNames.h',
'nsCoord.h',
'nsDeviceContext.h',
'nsFont.h',
'nsFontMetrics.h',
'nsGfxCIID.h',
'nsITheme.h',
'nsMargin.h',
'nsPoint.h',
'nsRect.h',
'nsRegion.h',
'nsRegionFwd.h',
'nsRenderingContext.h',
'nsSize.h',
'nsThemeConstants.h',
'nsTransform2D.h',
'PingPongRegion.h',
'RegionBuilder.h',
'X11UndefineNone.h'
]
EXPORTS.mozilla += [
'AppUnits.h',
'ArrayView.h',
]
EXPORTS.mozilla.gfx += [
'TiledRegion.h',
]
if CONFIG['MOZ_X11']:
EXPORTS.mozilla += ['X11Util.h']
SOURCES += [
'X11Util.cpp',
]
UNIFIED_SOURCES += [
'DriverCrashGuard.cpp',
'FilterSupport.cpp',
'gfxCrashReporterUtils.cpp',
'gfxTelemetry.cpp',
'nsColor.cpp',
'nsFont.cpp',
'nsFontMetrics.cpp',
'nsRect.cpp',
'nsRegion.cpp',
'nsScriptableRegion.cpp',
'nsThebesFontEnumerator.cpp',
'nsThebesGfxFactory.cpp',
'nsTransform2D.cpp',
'TiledRegion.cpp',
]
# nsDeviceContext.cpp cannot be built in unified mode because it pulls in OS X system headers.
SOURCES += [
'nsDeviceContext.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/dom/ipc', # for ContentChild.h
]
FINAL_LIBRARY = 'xul'
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
CXXFLAGS += CONFIG['TK_CFLAGS']
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']

View file

@ -0,0 +1,87 @@
/* -*- 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/. */
#ifndef __nsBoundingMetrics_h
#define __nsBoundingMetrics_h
#include "nsCoord.h"
#include <algorithm>
/* Struct used for accurate measurements of a string, in order to
* allow precise positioning when processing MathML. This is in its
* own header file because some very-widely-included headers need it
* but not the rest of nsFontMetrics, or vice versa.
*/
struct nsBoundingMetrics {
///////////
// Metrics that _exactly_ enclose the text:
// The character coordinate system is the one used on X Windows:
// 1. The origin is located at the intersection of the baseline
// with the left of the character's cell.
// 2. All horizontal bearings are oriented from left to right.
// 2. All horizontal bearings are oriented from left to right.
// 3. The ascent is oriented from bottom to top (being 0 at the orgin).
// 4. The descent is oriented from top to bottom (being 0 at the origin).
// Note that Win32/Mac/PostScript use a different convention for
// the descent (all vertical measurements are oriented from bottom
// to top on these palatforms). Make sure to flip the sign of the
// descent on these platforms for cross-platform compatibility.
// Any of the following member variables listed here can have
// positive or negative value.
nscoord leftBearing;
/* The horizontal distance from the origin of the drawing
operation to the left-most part of the drawn string. */
nscoord rightBearing;
/* The horizontal distance from the origin of the drawing
operation to the right-most part of the drawn string.
The _exact_ width of the string is therefore:
rightBearing - leftBearing */
nscoord ascent;
/* The vertical distance from the origin of the drawing
operation to the top-most part of the drawn string. */
nscoord descent;
/* The vertical distance from the origin of the drawing
operation to the bottom-most part of the drawn string.
The _exact_ height of the string is therefore:
ascent + descent */
nscoord width;
/* The horizontal distance from the origin of the drawing
operation to the correct origin for drawing another string
to follow the current one. Depending on the font, this
could be greater than or less than the right bearing. */
nsBoundingMetrics() : leftBearing(0), rightBearing(0),
ascent(0), descent(0), width(0)
{}
void
operator += (const nsBoundingMetrics& bm) {
if (ascent + descent == 0 && rightBearing - leftBearing == 0) {
ascent = bm.ascent;
descent = bm.descent;
leftBearing = width + bm.leftBearing;
rightBearing = width + bm.rightBearing;
}
else {
if (ascent < bm.ascent) ascent = bm.ascent;
if (descent < bm.descent) descent = bm.descent;
leftBearing = std::min(leftBearing, width + bm.leftBearing);
rightBearing = std::max(rightBearing, width + bm.rightBearing);
}
width += bm.width;
}
};
#endif // __nsBoundingMetrics_h

361
gfx/src/nsColor.cpp Normal file
View file

@ -0,0 +1,361 @@
/* -*- Mode: C++; tab-width: 2; 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 "mozilla/ArrayUtils.h" // for ArrayLength
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "mozilla/MathAlgorithms.h"
#include "nsColor.h"
#include <sys/types.h> // for int32_t
#include "nsColorNames.h" // for nsColorNames
#include "nsDebug.h" // for NS_ASSERTION, etc
#include "nsStaticNameTable.h"
#include "nsString.h" // for nsAutoCString, nsString, etc
#include "nscore.h" // for nsAString, etc
using namespace mozilla;
// define an array of all color names
#define GFX_COLOR(_name, _value) #_name,
static const char* const kColorNames[] = {
#include "nsColorNameList.h"
};
#undef GFX_COLOR
// define an array of all color name values
#define GFX_COLOR(_name, _value) _value,
static const nscolor kColors[] = {
#include "nsColorNameList.h"
};
#undef GFX_COLOR
#define eColorName_COUNT (ArrayLength(kColorNames))
#define eColorName_UNKNOWN (-1)
static nsStaticCaseInsensitiveNameTable* gColorTable = nullptr;
void nsColorNames::AddRefTable(void)
{
NS_ASSERTION(!gColorTable, "pre existing array!");
if (!gColorTable) {
gColorTable =
new nsStaticCaseInsensitiveNameTable(kColorNames, eColorName_COUNT);
}
}
void nsColorNames::ReleaseTable(void)
{
if (gColorTable) {
delete gColorTable;
gColorTable = nullptr;
}
}
static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, int dpc)
{
int component = 0;
int index = (color * dpc);
if (2 < dpc) {
dpc = 2;
}
while (--dpc >= 0) {
char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0');
if (('0' <= ch) && (ch <= '9')) {
component = (component * 16) + (ch - '0');
} else if ((('a' <= ch) && (ch <= 'f')) ||
(('A' <= ch) && (ch <= 'F'))) {
// "ch&7" handles lower and uppercase hex alphabetics
component = (component * 16) + (ch & 7) + 9;
}
else { // not a hex digit, treat it like 0
component = (component * 16);
}
}
return component;
}
bool
NS_HexToRGBA(const nsAString& aColorSpec, nsHexColorType aType,
nscolor* aResult)
{
const char16_t* buffer = aColorSpec.BeginReading();
int nameLen = aColorSpec.Length();
bool hasAlpha = false;
if (nameLen != 3 && nameLen != 6) {
if ((nameLen != 4 && nameLen != 8) || aType == nsHexColorType::NoAlpha) {
// Improperly formatted color value
return false;
}
hasAlpha = true;
}
// Make sure the digits are legal
for (int i = 0; i < nameLen; i++) {
char16_t ch = buffer[i];
if (((ch >= '0') && (ch <= '9')) ||
((ch >= 'a') && (ch <= 'f')) ||
((ch >= 'A') && (ch <= 'F'))) {
// Legal character
continue;
}
// Whoops. Illegal character.
return false;
}
// Convert the ascii to binary
int dpc = ((nameLen <= 4) ? 1 : 2);
// Translate components from hex to binary
int r = ComponentValue(buffer, nameLen, 0, dpc);
int g = ComponentValue(buffer, nameLen, 1, dpc);
int b = ComponentValue(buffer, nameLen, 2, dpc);
int a;
if (hasAlpha) {
a = ComponentValue(buffer, nameLen, 3, dpc);
} else {
a = (dpc == 1) ? 0xf : 0xff;
}
if (dpc == 1) {
// Scale single digit component to an 8 bit value. Replicate the
// single digit to compute the new value.
r = (r << 4) | r;
g = (g << 4) | g;
b = (b << 4) | b;
a = (a << 4) | a;
}
NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
NS_ASSERTION((a >= 0) && (a <= 255), "bad a");
*aResult = NS_RGBA(r, g, b, a);
return true;
}
// This implements part of the algorithm for legacy behavior described in
// http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value
bool NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult)
{
if (aColorSpec.EqualsLiteral("transparent")) {
return false;
}
int nameLen = aColorSpec.Length();
const char16_t* colorSpec = aColorSpec.get();
if (nameLen > 128) {
nameLen = 128;
}
if ('#' == colorSpec[0]) {
++colorSpec;
--nameLen;
}
// digits per component
int dpc = (nameLen + 2) / 3;
int newdpc = dpc;
// Use only the rightmost 8 characters of each component.
if (newdpc > 8) {
nameLen -= newdpc - 8;
colorSpec += newdpc - 8;
newdpc = 8;
}
// And then keep trimming characters at the left until we'd trim one
// that would leave a nonzero value, but not past 2 characters per
// component.
while (newdpc > 2) {
bool haveNonzero = false;
for (int c = 0; c < 3; ++c) {
MOZ_ASSERT(c * dpc < nameLen,
"should not pass end of string while newdpc > 2");
char16_t ch = colorSpec[c * dpc];
if (('1' <= ch && ch <= '9') ||
('A' <= ch && ch <= 'F') ||
('a' <= ch && ch <= 'f')) {
haveNonzero = true;
break;
}
}
if (haveNonzero) {
break;
}
--newdpc;
--nameLen;
++colorSpec;
}
// Translate components from hex to binary
int r = ComponentValue(colorSpec, nameLen, 0, dpc);
int g = ComponentValue(colorSpec, nameLen, 1, dpc);
int b = ComponentValue(colorSpec, nameLen, 2, dpc);
NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
*aResult = NS_RGB(r, g, b);
return true;
}
bool NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
{
if (!gColorTable) return false;
int32_t id = gColorTable->Lookup(aColorName);
if (eColorName_UNKNOWN < id) {
NS_ASSERTION(uint32_t(id) < eColorName_COUNT,
"gColorTable->Lookup messed up");
if (aResult) {
*aResult = kColors[id];
}
return true;
}
return false;
}
// Returns kColorNames, an array of all possible color names, and sets
// *aSizeArray to the size of that array. Do NOT call free() on this array.
const char * const * NS_AllColorNames(size_t *aSizeArray)
{
*aSizeArray = ArrayLength(kColorNames);
return kColorNames;
}
// Macro to blend two colors
//
// equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
#define MOZ_BLEND(target, bg, fg, fgalpha) \
FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha))
nscolor
NS_ComposeColors(nscolor aBG, nscolor aFG)
{
// This function uses colors that are non premultiplied alpha.
int r, g, b, a;
int bgAlpha = NS_GET_A(aBG);
int fgAlpha = NS_GET_A(aFG);
// Compute the final alpha of the blended color
// a = fgAlpha + bgAlpha*(255 - fgAlpha)/255;
FAST_DIVIDE_BY_255(a, bgAlpha*(255-fgAlpha));
a = fgAlpha + a;
int blendAlpha;
if (a == 0) {
// In this case the blended color is totally trasparent,
// we preserve the color information of the foreground color.
blendAlpha = 255;
} else {
blendAlpha = (fgAlpha*255)/a;
}
MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha);
MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha);
MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha);
return NS_RGBA(r, g, b, a);
}
namespace mozilla {
static uint32_t
BlendColorComponent(uint32_t aBg, uint32_t aFg, uint32_t aFgAlpha)
{
return RoundingDivideBy255(aBg * (255 - aFgAlpha) + aFg * aFgAlpha);
}
nscolor
LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio)
{
// Common case that either pure background or pure foreground
if (aFgRatio == 0) {
return aBg;
}
if (aFgRatio == 255) {
return aFg;
}
// Common case that alpha channel is equal (usually both are opaque)
if (NS_GET_A(aBg) == NS_GET_A(aFg)) {
auto r = BlendColorComponent(NS_GET_R(aBg), NS_GET_R(aFg), aFgRatio);
auto g = BlendColorComponent(NS_GET_G(aBg), NS_GET_G(aFg), aFgRatio);
auto b = BlendColorComponent(NS_GET_B(aBg), NS_GET_B(aFg), aFgRatio);
return NS_RGBA(r, g, b, NS_GET_A(aFg));
}
constexpr float kFactor = 1.0f / 255.0f;
float p1 = kFactor * (255 - aFgRatio);
float a1 = kFactor * NS_GET_A(aBg);
float r1 = a1 * NS_GET_R(aBg);
float g1 = a1 * NS_GET_G(aBg);
float b1 = a1 * NS_GET_B(aBg);
float p2 = 1.0f - p1;
float a2 = kFactor * NS_GET_A(aFg);
float r2 = a2 * NS_GET_R(aFg);
float g2 = a2 * NS_GET_G(aFg);
float b2 = a2 * NS_GET_B(aFg);
float a = p1 * a1 + p2 * a2;
if (a == 0.0) {
return NS_RGBA(0, 0, 0, 0);
}
auto r = ClampColor((p1 * r1 + p2 * r2) / a);
auto g = ClampColor((p1 * g1 + p2 * g2) / a);
auto b = ClampColor((p1 * b1 + p2 * b2) / a);
return NS_RGBA(r, g, b, NSToIntRound(a * 255));
}
} // namespace mozilla
// Functions to convert from HSL color space to RGB color space.
// This is the algorithm described in the CSS3 specification
// helper
static float
HSL_HueToRGB(float m1, float m2, float h)
{
if (h < 0.0f)
h += 1.0f;
if (h > 1.0f)
h -= 1.0f;
if (h < (float)(1.0/6.0))
return m1 + (m2 - m1)*h*6.0f;
if (h < (float)(1.0/2.0))
return m2;
if (h < (float)(2.0/3.0))
return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f;
return m1;
}
// The float parameters are all expected to be in the range 0-1
nscolor
NS_HSL2RGB(float h, float s, float l)
{
uint8_t r, g, b;
float m1, m2;
if (l <= 0.5f) {
m2 = l*(s+1);
} else {
m2 = l + s - l*s;
}
m1 = l*2 - m2;
r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f));
g = uint8_t(255 * HSL_HueToRGB(m1, m2, h));
b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f));
return NS_RGB(r, g, b);
}
const char*
NS_RGBToColorName(nscolor aColor)
{
for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) {
if (kColors[idx] == aColor) {
return kColorNames[idx];
}
}
return nullptr;
}

123
gfx/src/nsColor.h Normal file
View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef nsColor_h___
#define nsColor_h___
#include <stddef.h> // for size_t
#include <stdint.h> // for uint8_t, uint32_t
#include "nscore.h" // for nsAString
#include "nsCoord.h" // for NSToIntRound
class nsAString;
class nsString;
// A color is a 32 bit unsigned integer with four components: R, G, B
// and A.
typedef uint32_t nscolor;
// Make a color out of r,g,b values. This assumes that the r,g,b values are
// properly constrained to 0-255. This also assumes that a is 255.
#define NS_RGB(_r,_g,_b) \
((nscolor) ((255 << 24) | ((_b)<<16) | ((_g)<<8) | (_r)))
// Make a color out of r,g,b,a values. This assumes that the r,g,b,a
// values are properly constrained to 0-255.
#define NS_RGBA(_r,_g,_b,_a) \
((nscolor) (((_a) << 24) | ((_b)<<16) | ((_g)<<8) | (_r)))
// Extract color components from nscolor
#define NS_GET_R(_rgba) ((uint8_t) ((_rgba) & 0xff))
#define NS_GET_G(_rgba) ((uint8_t) (((_rgba) >> 8) & 0xff))
#define NS_GET_B(_rgba) ((uint8_t) (((_rgba) >> 16) & 0xff))
#define NS_GET_A(_rgba) ((uint8_t) (((_rgba) >> 24) & 0xff))
namespace mozilla {
template<typename T>
inline uint8_t ClampColor(T aColor)
{
if (aColor >= 255) {
return 255;
}
if (aColor <= 0) {
return 0;
}
return NSToIntRound(aColor);
}
} // namespace mozilla
// Fast approximate division by 255. It has the property that
// for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
// But it only uses two adds and two shifts instead of an
// integer division (which is expensive on many processors).
//
// equivalent to target=v/255
#define FAST_DIVIDE_BY_255(target,v) \
PR_BEGIN_MACRO \
unsigned tmp_ = v; \
target = ((tmp_ << 8) + tmp_ + 255) >> 16; \
PR_END_MACRO
enum class nsHexColorType : uint8_t {
NoAlpha, // 3 or 6 digit hex colors only
AllowAlpha, // 3, 4, 6, or 8 digit hex colors
};
// Translate a hex string to a color. Return true if it parses ok,
// otherwise return false.
// This accepts the number of digits specified by aType.
bool
NS_HexToRGBA(const nsAString& aBuf, nsHexColorType aType, nscolor* aResult);
// Compose one NS_RGB color onto another. The result is what
// you get if you draw aFG on top of aBG with operator OVER.
nscolor NS_ComposeColors(nscolor aBG, nscolor aFG);
namespace mozilla {
inline uint32_t RoundingDivideBy255(uint32_t n)
{
// There is an approximate alternative: ((n << 8) + n + 32896) >> 16
// But that is actually slower than this simple expression on a modern
// machine with a modern compiler.
return (n + 127) / 255;
}
// Blend one RGBA color with another based on a given ratio.
// It is a linear interpolation on each channel with alpha premultipled.
nscolor LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio);
} // namespace mozilla
// Translate a hex string to a color. Return true if it parses ok,
// otherwise return false.
// This version accepts 1 to 9 digits (missing digits are 0)
bool NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
// There is no function to translate a color to a hex string, because
// the hex-string syntax does not support transparency.
// Translate a color name to a color. Return true if it parses ok,
// otherwise return false.
bool NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
// Returns an array of all possible color names, and sets
// *aSizeArray to the size of that array. Do NOT call |free()| on this array.
const char * const * NS_AllColorNames(size_t *aSizeArray);
// function to convert from HSL color space to RGB color space
// the float parameters are all expected to be in the range 0-1
nscolor NS_HSL2RGB(float h, float s, float l);
// Return a color name for the given nscolor. If there is no color
// name for it, returns null. If there are multiple possible color
// names for the given color, the first one in nsColorNameList.h
// (which is generally the first one in alphabetical order) will be
// returned.
const char* NS_RGBToColorName(nscolor aColor);
#endif /* nsColor_h___ */

181
gfx/src/nsColorNameList.h Normal file
View file

@ -0,0 +1,181 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
/******
This file contains the list of all named colors
See nsCSSColorNames.h for access to the enum values for colors
It is designed to be used as inline input to nsCSSColorNames.cpp *only*
through the magic of C preprocessing.
All entries must be enclosed in the macro GFX_COLOR which will have cruel
and unusual things done to it
It is recommended (but not strictly necessary) to keep all entries
in alphabetical order
The first argument to GFX_COLOR is both the enum identifier of the color
and the string value
The second argument is the sRGBA value for the named color
'name' entries *must* use only lowercase characters.
** Break these invarient and bad things will happen. **
******/
GFX_COLOR(aliceblue, NS_RGB(240, 248, 255))
GFX_COLOR(antiquewhite, NS_RGB(250, 235, 215))
GFX_COLOR(aqua, NS_RGB( 0, 255, 255))
GFX_COLOR(aquamarine, NS_RGB(127, 255, 212))
GFX_COLOR(azure, NS_RGB(240, 255, 255))
GFX_COLOR(beige, NS_RGB(245, 245, 220))
GFX_COLOR(bisque, NS_RGB(255, 228, 196))
GFX_COLOR(black, NS_RGB( 0, 0, 0))
GFX_COLOR(blanchedalmond, NS_RGB(255, 235, 205))
GFX_COLOR(blue, NS_RGB( 0, 0, 255))
GFX_COLOR(blueviolet, NS_RGB(138, 43, 226))
GFX_COLOR(brown, NS_RGB(165, 42, 42))
GFX_COLOR(burlywood, NS_RGB(222, 184, 135))
GFX_COLOR(cadetblue, NS_RGB( 95, 158, 160))
GFX_COLOR(chartreuse, NS_RGB(127, 255, 0))
GFX_COLOR(chocolate, NS_RGB(210, 105, 30))
GFX_COLOR(coral, NS_RGB(255, 127, 80))
GFX_COLOR(cornflowerblue, NS_RGB(100, 149, 237))
GFX_COLOR(cornsilk, NS_RGB(255, 248, 220))
GFX_COLOR(crimson, NS_RGB(220, 20, 60))
GFX_COLOR(cyan, NS_RGB( 0, 255, 255))
GFX_COLOR(darkblue, NS_RGB( 0, 0, 139))
GFX_COLOR(darkcyan, NS_RGB( 0, 139, 139))
GFX_COLOR(darkgoldenrod, NS_RGB(184, 134, 11))
GFX_COLOR(darkgray, NS_RGB(169, 169, 169))
GFX_COLOR(darkgreen, NS_RGB( 0, 100, 0))
GFX_COLOR(darkgrey, NS_RGB(169, 169, 169))
GFX_COLOR(darkkhaki, NS_RGB(189, 183, 107))
GFX_COLOR(darkmagenta, NS_RGB(139, 0, 139))
GFX_COLOR(darkolivegreen, NS_RGB( 85, 107, 47))
GFX_COLOR(darkorange, NS_RGB(255, 140, 0))
GFX_COLOR(darkorchid, NS_RGB(153, 50, 204))
GFX_COLOR(darkred, NS_RGB(139, 0, 0))
GFX_COLOR(darksalmon, NS_RGB(233, 150, 122))
GFX_COLOR(darkseagreen, NS_RGB(143, 188, 143))
GFX_COLOR(darkslateblue, NS_RGB( 72, 61, 139))
GFX_COLOR(darkslategray, NS_RGB( 47, 79, 79))
GFX_COLOR(darkslategrey, NS_RGB( 47, 79, 79))
GFX_COLOR(darkturquoise, NS_RGB( 0, 206, 209))
GFX_COLOR(darkviolet, NS_RGB(148, 0, 211))
GFX_COLOR(deeppink, NS_RGB(255, 20, 147))
GFX_COLOR(deepskyblue, NS_RGB( 0, 191, 255))
GFX_COLOR(dimgray, NS_RGB(105, 105, 105))
GFX_COLOR(dimgrey, NS_RGB(105, 105, 105))
GFX_COLOR(dodgerblue, NS_RGB( 30, 144, 255))
GFX_COLOR(firebrick, NS_RGB(178, 34, 34))
GFX_COLOR(floralwhite, NS_RGB(255, 250, 240))
GFX_COLOR(forestgreen, NS_RGB( 34, 139, 34))
GFX_COLOR(fuchsia, NS_RGB(255, 0, 255))
GFX_COLOR(gainsboro, NS_RGB(220, 220, 220))
GFX_COLOR(ghostwhite, NS_RGB(248, 248, 255))
GFX_COLOR(gold, NS_RGB(255, 215, 0))
GFX_COLOR(goldenrod, NS_RGB(218, 165, 32))
GFX_COLOR(gray, NS_RGB(128, 128, 128))
GFX_COLOR(grey, NS_RGB(128, 128, 128))
GFX_COLOR(green, NS_RGB( 0, 128, 0))
GFX_COLOR(greenyellow, NS_RGB(173, 255, 47))
GFX_COLOR(honeydew, NS_RGB(240, 255, 240))
GFX_COLOR(hotpink, NS_RGB(255, 105, 180))
GFX_COLOR(indianred, NS_RGB(205, 92, 92))
GFX_COLOR(indigo, NS_RGB( 75, 0, 130))
GFX_COLOR(ivory, NS_RGB(255, 255, 240))
GFX_COLOR(khaki, NS_RGB(240, 230, 140))
GFX_COLOR(lavender, NS_RGB(230, 230, 250))
GFX_COLOR(lavenderblush, NS_RGB(255, 240, 245))
GFX_COLOR(lawngreen, NS_RGB(124, 252, 0))
GFX_COLOR(lemonchiffon, NS_RGB(255, 250, 205))
GFX_COLOR(lightblue, NS_RGB(173, 216, 230))
GFX_COLOR(lightcoral, NS_RGB(240, 128, 128))
GFX_COLOR(lightcyan, NS_RGB(224, 255, 255))
GFX_COLOR(lightgoldenrodyellow, NS_RGB(250, 250, 210))
GFX_COLOR(lightgray, NS_RGB(211, 211, 211))
GFX_COLOR(lightgreen, NS_RGB(144, 238, 144))
GFX_COLOR(lightgrey, NS_RGB(211, 211, 211))
GFX_COLOR(lightpink, NS_RGB(255, 182, 193))
GFX_COLOR(lightsalmon, NS_RGB(255, 160, 122))
GFX_COLOR(lightseagreen, NS_RGB( 32, 178, 170))
GFX_COLOR(lightskyblue, NS_RGB(135, 206, 250))
GFX_COLOR(lightslategray, NS_RGB(119, 136, 153))
GFX_COLOR(lightslategrey, NS_RGB(119, 136, 153))
GFX_COLOR(lightsteelblue, NS_RGB(176, 196, 222))
GFX_COLOR(lightyellow, NS_RGB(255, 255, 224))
GFX_COLOR(lime, NS_RGB( 0, 255, 0))
GFX_COLOR(limegreen, NS_RGB( 50, 205, 50))
GFX_COLOR(linen, NS_RGB(250, 240, 230))
GFX_COLOR(magenta, NS_RGB(255, 0, 255))
GFX_COLOR(maroon, NS_RGB(128, 0, 0))
GFX_COLOR(mediumaquamarine, NS_RGB(102, 205, 170))
GFX_COLOR(mediumblue, NS_RGB( 0, 0, 205))
GFX_COLOR(mediumorchid, NS_RGB(186, 85, 211))
GFX_COLOR(mediumpurple, NS_RGB(147, 112, 219))
GFX_COLOR(mediumseagreen, NS_RGB( 60, 179, 113))
GFX_COLOR(mediumslateblue, NS_RGB(123, 104, 238))
GFX_COLOR(mediumspringgreen, NS_RGB( 0, 250, 154))
GFX_COLOR(mediumturquoise, NS_RGB( 72, 209, 204))
GFX_COLOR(mediumvioletred, NS_RGB(199, 21, 133))
GFX_COLOR(midnightblue, NS_RGB( 25, 25, 112))
GFX_COLOR(mintcream, NS_RGB(245, 255, 250))
GFX_COLOR(mistyrose, NS_RGB(255, 228, 225))
GFX_COLOR(moccasin, NS_RGB(255, 228, 181))
GFX_COLOR(navajowhite, NS_RGB(255, 222, 173))
GFX_COLOR(navy, NS_RGB( 0, 0, 128))
GFX_COLOR(oldlace, NS_RGB(253, 245, 230))
GFX_COLOR(olive, NS_RGB(128, 128, 0))
GFX_COLOR(olivedrab, NS_RGB(107, 142, 35))
GFX_COLOR(orange, NS_RGB(255, 165, 0))
GFX_COLOR(orangered, NS_RGB(255, 69, 0))
GFX_COLOR(orchid, NS_RGB(218, 112, 214))
GFX_COLOR(palegoldenrod, NS_RGB(238, 232, 170))
GFX_COLOR(palegreen, NS_RGB(152, 251, 152))
GFX_COLOR(paleturquoise, NS_RGB(175, 238, 238))
GFX_COLOR(palevioletred, NS_RGB(219, 112, 147))
GFX_COLOR(papayawhip, NS_RGB(255, 239, 213))
GFX_COLOR(peachpuff, NS_RGB(255, 218, 185))
GFX_COLOR(peru, NS_RGB(205, 133, 63))
GFX_COLOR(pink, NS_RGB(255, 192, 203))
GFX_COLOR(plum, NS_RGB(221, 160, 221))
GFX_COLOR(powderblue, NS_RGB(176, 224, 230))
GFX_COLOR(purple, NS_RGB(128, 0, 128))
GFX_COLOR(rebeccapurple, NS_RGB(102, 51, 153))
GFX_COLOR(red, NS_RGB(255, 0, 0))
GFX_COLOR(rosybrown, NS_RGB(188, 143, 143))
GFX_COLOR(royalblue, NS_RGB( 65, 105, 225))
GFX_COLOR(saddlebrown, NS_RGB(139, 69, 19))
GFX_COLOR(salmon, NS_RGB(250, 128, 114))
GFX_COLOR(sandybrown, NS_RGB(244, 164, 96))
GFX_COLOR(seagreen, NS_RGB( 46, 139, 87))
GFX_COLOR(seashell, NS_RGB(255, 245, 238))
GFX_COLOR(sienna, NS_RGB(160, 82, 45))
GFX_COLOR(silver, NS_RGB(192, 192, 192))
GFX_COLOR(skyblue, NS_RGB(135, 206, 235))
GFX_COLOR(slateblue, NS_RGB(106, 90, 205))
GFX_COLOR(slategray, NS_RGB(112, 128, 144))
GFX_COLOR(slategrey, NS_RGB(112, 128, 144))
GFX_COLOR(snow, NS_RGB(255, 250, 250))
GFX_COLOR(springgreen, NS_RGB( 0, 255, 127))
GFX_COLOR(steelblue, NS_RGB( 70, 130, 180))
GFX_COLOR(tan, NS_RGB(210, 180, 140))
GFX_COLOR(teal, NS_RGB( 0, 128, 128))
GFX_COLOR(thistle, NS_RGB(216, 191, 216))
GFX_COLOR(tomato, NS_RGB(255, 99, 71))
GFX_COLOR(transparent, NS_RGBA(0, 0, 0, 0))
GFX_COLOR(turquoise, NS_RGB( 64, 224, 208))
GFX_COLOR(violet, NS_RGB(238, 130, 238))
GFX_COLOR(wheat, NS_RGB(245, 222, 179))
GFX_COLOR(white, NS_RGB(255, 255, 255))
GFX_COLOR(whitesmoke, NS_RGB(245, 245, 245))
GFX_COLOR(yellow, NS_RGB(255, 255, 0))
GFX_COLOR(yellowgreen, NS_RGB(154, 205, 50))

16
gfx/src/nsColorNames.h Normal file
View file

@ -0,0 +1,16 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef nsColorNames_h___
#define nsColorNames_h___
class nsColorNames {
public:
static void AddRefTable(void);
static void ReleaseTable(void);
};
#endif /* nsColorNames_h___ */

443
gfx/src/nsCoord.h Normal file
View file

@ -0,0 +1,443 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef NSCOORD_H
#define NSCOORD_H
#include "nsAlgorithm.h"
#include "nscore.h"
#include "nsMathUtils.h"
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include "nsDebug.h"
#include <algorithm>
/*
* Basic type used for the geometry classes.
*
* Normally all coordinates are maintained in an app unit coordinate
* space. An app unit is 1/60th of a CSS device pixel, which is, in turn
* an integer number of device pixels, such at the CSS DPI is as close to
* 96dpi as possible.
*/
// This controls whether we're using integers or floats for coordinates. We
// want to eventually use floats.
//#define NS_COORD_IS_FLOAT
inline float NS_IEEEPositiveInfinity() {
union { uint32_t mPRUint32; float mFloat; } pun;
pun.mPRUint32 = 0x7F800000;
return pun.mFloat;
}
inline bool NS_IEEEIsNan(float aF) {
union { uint32_t mBits; float mFloat; } pun;
pun.mFloat = aF;
return (pun.mBits & 0x7F800000) == 0x7F800000 &&
(pun.mBits & 0x007FFFFF) != 0;
}
#ifdef NS_COORD_IS_FLOAT
typedef float nscoord;
#define nscoord_MAX NS_IEEEPositiveInfinity()
#else
typedef int32_t nscoord;
#define nscoord_MAX nscoord(1 << 30)
#endif
#define nscoord_MIN (-nscoord_MAX)
inline void VERIFY_COORD(nscoord aCoord) {
#ifdef NS_COORD_IS_FLOAT
NS_ASSERTION(floorf(aCoord) == aCoord,
"Coords cannot have fractions");
#endif
}
/**
* Divide aSpace by aN. Assign the resulting quotient to aQuotient and
* return the remainder.
*/
inline nscoord NSCoordDivRem(nscoord aSpace, size_t aN, nscoord* aQuotient)
{
#ifdef NS_COORD_IS_FLOAT
*aQuotient = aSpace / aN;
return 0.0f;
#else
div_t result = div(aSpace, aN);
*aQuotient = nscoord(result.quot);
return nscoord(result.rem);
#endif
}
inline nscoord NSCoordMulDiv(nscoord aMult1, nscoord aMult2, nscoord aDiv) {
#ifdef NS_COORD_IS_FLOAT
return (aMult1 * aMult2 / aDiv);
#else
return (int64_t(aMult1) * int64_t(aMult2) / int64_t(aDiv));
#endif
}
inline nscoord NSToCoordRound(float aValue)
{
#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && !defined(__clang__)
return NS_lroundup30(aValue);
#else
return nscoord(floorf(aValue + 0.5f));
#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
}
inline nscoord NSToCoordRound(double aValue)
{
#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && !defined(__clang__)
return NS_lroundup30((float)aValue);
#else
return nscoord(floor(aValue + 0.5f));
#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
}
inline nscoord NSToCoordRoundWithClamp(float aValue)
{
#ifndef NS_COORD_IS_FLOAT
// Bounds-check before converting out of float, to avoid overflow
if (aValue >= nscoord_MAX) {
return nscoord_MAX;
}
if (aValue <= nscoord_MIN) {
return nscoord_MIN;
}
#endif
return NSToCoordRound(aValue);
}
/**
* Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
* appropriate for the signs of aCoord and aScale. If requireNotNegative is
* true, this method will enforce that aScale is not negative; use that
* parametrization to get a check of that fact in debug builds.
*/
inline nscoord _nscoordSaturatingMultiply(nscoord aCoord, float aScale,
bool requireNotNegative) {
VERIFY_COORD(aCoord);
if (requireNotNegative) {
MOZ_ASSERT(aScale >= 0.0f,
"negative scaling factors must be handled manually");
}
#ifdef NS_COORD_IS_FLOAT
return floorf(aCoord * aScale);
#else
float product = aCoord * aScale;
if (requireNotNegative ? aCoord > 0 : (aCoord > 0) == (aScale > 0))
return NSToCoordRoundWithClamp(std::min<float>(nscoord_MAX, product));
return NSToCoordRoundWithClamp(std::max<float>(nscoord_MIN, product));
#endif
}
/**
* Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
* appropriate for the sign of aCoord. This method requires aScale to not be
* negative; use this method when you know that aScale should never be
* negative to get a sanity check of that invariant in debug builds.
*/
inline nscoord NSCoordSaturatingNonnegativeMultiply(nscoord aCoord, float aScale) {
return _nscoordSaturatingMultiply(aCoord, aScale, true);
}
/**
* Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
* appropriate for the signs of aCoord and aScale.
*/
inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aScale) {
return _nscoordSaturatingMultiply(aCoord, aScale, false);
}
/**
* Returns a + b, capping the sum to nscoord_MAX.
*
* This function assumes that neither argument is nscoord_MIN.
*
* Note: If/when we start using floats for nscoords, this function won't be as
* necessary. Normal float addition correctly handles adding with infinity,
* assuming we aren't adding nscoord_MIN. (-infinity)
*/
inline nscoord
NSCoordSaturatingAdd(nscoord a, nscoord b)
{
VERIFY_COORD(a);
VERIFY_COORD(b);
#ifdef NS_COORD_IS_FLOAT
// Float math correctly handles a+b, given that neither is -infinity.
return a + b;
#else
if (a == nscoord_MAX || b == nscoord_MAX) {
// infinity + anything = anything + infinity = infinity
return nscoord_MAX;
} else {
// a + b = a + b
// Cap the result, just in case we're dealing with numbers near nscoord_MAX
return std::min(nscoord_MAX, a + b);
}
#endif
}
/**
* Returns a - b, gracefully handling cases involving nscoord_MAX.
* This function assumes that neither argument is nscoord_MIN.
*
* The behavior is as follows:
*
* a) infinity - infinity -> infMinusInfResult
* b) N - infinity -> 0 (unexpected -- triggers NOTREACHED)
* c) infinity - N -> infinity
* d) N1 - N2 -> N1 - N2
*
* Note: For float nscoords, cases (c) and (d) are handled by normal float
* math. We still need to explicitly specify the behavior for cases (a)
* and (b), though. (Under normal float math, those cases would return NaN
* and -infinity, respectively.)
*/
inline nscoord
NSCoordSaturatingSubtract(nscoord a, nscoord b,
nscoord infMinusInfResult)
{
VERIFY_COORD(a);
VERIFY_COORD(b);
if (b == nscoord_MAX) {
if (a == nscoord_MAX) {
// case (a)
return infMinusInfResult;
} else {
// case (b)
NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]");
return 0;
}
} else {
#ifdef NS_COORD_IS_FLOAT
// case (c) and (d) for floats. (float math handles both)
return a - b;
#else
if (a == nscoord_MAX) {
// case (c) for integers
return nscoord_MAX;
} else {
// case (d) for integers
// Cap the result, in case we're dealing with numbers near nscoord_MAX
return std::min(nscoord_MAX, a - b);
}
#endif
}
}
inline float NSCoordToFloat(nscoord aCoord) {
VERIFY_COORD(aCoord);
#ifdef NS_COORD_IS_FLOAT
NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in float conversion");
#endif
return (float)aCoord;
}
/*
* Coord Rounding Functions
*/
inline nscoord NSToCoordFloor(float aValue)
{
return nscoord(floorf(aValue));
}
inline nscoord NSToCoordFloor(double aValue)
{
return nscoord(floor(aValue));
}
inline nscoord NSToCoordFloorClamped(float aValue)
{
#ifndef NS_COORD_IS_FLOAT
// Bounds-check before converting out of float, to avoid overflow
if (aValue >= nscoord_MAX) {
return nscoord_MAX;
}
if (aValue <= nscoord_MIN) {
return nscoord_MIN;
}
#endif
return NSToCoordFloor(aValue);
}
inline nscoord NSToCoordCeil(float aValue)
{
return nscoord(ceilf(aValue));
}
inline nscoord NSToCoordCeil(double aValue)
{
return nscoord(ceil(aValue));
}
inline nscoord NSToCoordCeilClamped(double aValue)
{
#ifndef NS_COORD_IS_FLOAT
// Bounds-check before converting out of double, to avoid overflow
if (aValue >= nscoord_MAX) {
return nscoord_MAX;
}
if (aValue <= nscoord_MIN) {
return nscoord_MIN;
}
#endif
return NSToCoordCeil(aValue);
}
// The NSToCoordTrunc* functions remove the fractional component of
// aValue, and are thus equivalent to NSToCoordFloor* for positive
// values and NSToCoordCeil* for negative values.
inline nscoord NSToCoordTrunc(float aValue)
{
// There's no need to use truncf() since it matches the default
// rules for float to integer conversion.
return nscoord(aValue);
}
inline nscoord NSToCoordTrunc(double aValue)
{
// There's no need to use trunc() since it matches the default
// rules for float to integer conversion.
return nscoord(aValue);
}
inline nscoord NSToCoordTruncClamped(float aValue)
{
#ifndef NS_COORD_IS_FLOAT
// Bounds-check before converting out of float, to avoid overflow
if (aValue >= nscoord_MAX) {
return nscoord_MAX;
}
if (aValue <= nscoord_MIN) {
return nscoord_MIN;
}
#endif
return NSToCoordTrunc(aValue);
}
inline nscoord NSToCoordTruncClamped(double aValue)
{
#ifndef NS_COORD_IS_FLOAT
// Bounds-check before converting out of double, to avoid overflow
if (aValue >= nscoord_MAX) {
return nscoord_MAX;
}
if (aValue <= nscoord_MIN) {
return nscoord_MIN;
}
#endif
return NSToCoordTrunc(aValue);
}
/*
* Int Rounding Functions
*/
inline int32_t NSToIntFloor(float aValue)
{
return int32_t(floorf(aValue));
}
inline int32_t NSToIntCeil(float aValue)
{
return int32_t(ceilf(aValue));
}
inline int32_t NSToIntRound(float aValue)
{
return NS_lroundf(aValue);
}
inline int32_t NSToIntRound(double aValue)
{
return NS_lround(aValue);
}
inline int32_t NSToIntRoundUp(double aValue)
{
return int32_t(floor(aValue + 0.5));
}
/*
* App Unit/Pixel conversions
*/
inline nscoord NSFloatPixelsToAppUnits(float aPixels, float aAppUnitsPerPixel)
{
return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel);
}
inline nscoord NSIntPixelsToAppUnits(int32_t aPixels, int32_t aAppUnitsPerPixel)
{
// The cast to nscoord makes sure we don't overflow if we ever change
// nscoord to float
nscoord r = aPixels * (nscoord)aAppUnitsPerPixel;
VERIFY_COORD(r);
return r;
}
inline float NSAppUnitsToFloatPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
{
return (float(aAppUnits) / aAppUnitsPerPixel);
}
inline double NSAppUnitsToDoublePixels(nscoord aAppUnits, double aAppUnitsPerPixel)
{
return (double(aAppUnits) / aAppUnitsPerPixel);
}
inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
{
return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel);
}
inline float NSCoordScale(nscoord aCoord, int32_t aFromAPP, int32_t aToAPP)
{
return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP;
}
/// handy constants
#define TWIPS_PER_POINT_INT 20
#define TWIPS_PER_POINT_FLOAT 20.0f
#define POINTS_PER_INCH_INT 72
#define POINTS_PER_INCH_FLOAT 72.0f
#define CM_PER_INCH_FLOAT 2.54f
#define MM_PER_INCH_FLOAT 25.4f
/*
* Twips/unit conversions
*/
inline float NSUnitsToTwips(float aValue, float aPointsPerUnit)
{
return aValue * aPointsPerUnit * TWIPS_PER_POINT_FLOAT;
}
inline float NSTwipsToUnits(float aTwips, float aUnitsPerPoint)
{
return (aTwips * (aUnitsPerPoint / TWIPS_PER_POINT_FLOAT));
}
/// Unit conversion macros
//@{
#define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f)
#define NS_INCHES_TO_TWIPS(x) NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch
#define NS_MILLIMETERS_TO_TWIPS(x) NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f))
#define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x))
#define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x))
#define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT)
#define NS_TWIPS_TO_MILLIMETERS(x) NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f))
//@}
#endif /* NSCOORD_H */

725
gfx/src/nsDeviceContext.cpp Normal file
View file

@ -0,0 +1,725 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 ts=4 expandtab: */
/* 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 "nsDeviceContext.h"
#include <algorithm> // for max
#include "gfxASurface.h" // for gfxASurface, etc
#include "gfxContext.h"
#include "gfxFont.h" // for gfxFontGroup
#include "gfxImageSurface.h" // for gfxImageSurface
#include "gfxPoint.h" // for gfxSize
#include "mozilla/Attributes.h" // for final
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/gfx/PrintTarget.h"
#include "mozilla/Preferences.h" // for Preferences
#include "mozilla/Services.h" // for GetObserverService
#include "mozilla/mozalloc.h" // for operator new
#include "nsCRT.h" // for nsCRT
#include "nsDebug.h" // for NS_NOTREACHED, NS_ASSERTION, etc
#include "nsFont.h" // for nsFont
#include "nsFontMetrics.h" // for nsFontMetrics
#include "nsIAtom.h" // for nsIAtom, NS_Atomize
#include "nsID.h"
#include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
#include "nsILanguageAtomService.h" // for nsILanguageAtomService, etc
#include "nsIObserver.h" // for nsIObserver, etc
#include "nsIObserverService.h" // for nsIObserverService
#include "nsIScreen.h" // for nsIScreen
#include "nsIScreenManager.h" // for nsIScreenManager
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
#include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
#include "nsRect.h" // for nsRect
#include "nsServiceManagerUtils.h" // for do_GetService
#include "nsString.h" // for nsDependentString
#include "nsTArray.h" // for nsTArray, nsTArray_Impl
#include "nsThreadUtils.h" // for NS_IsMainThread
#include "mozilla/gfx/Logging.h"
using namespace mozilla;
using namespace mozilla::gfx;
using mozilla::services::GetObserverService;
class nsFontCache final : public nsIObserver
{
public:
nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); }
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
void Init(nsDeviceContext* aContext);
void Destroy();
already_AddRefed<nsFontMetrics> GetMetricsFor(
const nsFont& aFont, const nsFontMetrics::Params& aParams);
void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
void Compact();
void Flush();
protected:
~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); }
nsDeviceContext* mContext; // owner
nsCOMPtr<nsIAtom> mLocaleLanguage;
nsTArray<nsFontMetrics*> mFontMetrics;
};
NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver)
// The Init and Destroy methods are necessary because it's not
// safe to call AddObserver from a constructor or RemoveObserver
// from a destructor. That should be fixed.
void
nsFontCache::Init(nsDeviceContext* aContext)
{
mContext = aContext;
// register as a memory-pressure observer to free font resources
// in low-memory situations.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (obs)
obs->AddObserver(this, "memory-pressure", false);
nsCOMPtr<nsILanguageAtomService> langService;
langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
if (langService) {
mLocaleLanguage = langService->GetLocaleLanguage();
}
if (!mLocaleLanguage) {
mLocaleLanguage = NS_Atomize("x-western");
}
}
void
nsFontCache::Destroy()
{
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (obs)
obs->RemoveObserver(this, "memory-pressure");
Flush();
}
NS_IMETHODIMP
nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
{
if (!nsCRT::strcmp(aTopic, "memory-pressure"))
Compact();
return NS_OK;
}
already_AddRefed<nsFontMetrics>
nsFontCache::GetMetricsFor(const nsFont& aFont,
const nsFontMetrics::Params& aParams)
{
nsIAtom* language = aParams.language ? aParams.language
: mLocaleLanguage.get();
// First check our cache
// start from the end, which is where we put the most-recent-used element
int32_t n = mFontMetrics.Length() - 1;
for (int32_t i = n; i >= 0; --i) {
nsFontMetrics* fm = mFontMetrics[i];
if (fm->Font().Equals(aFont) &&
fm->GetUserFontSet() == aParams.userFontSet &&
fm->Language() == language &&
fm->Orientation() == aParams.orientation) {
if (i != n) {
// promote it to the end of the cache
mFontMetrics.RemoveElementAt(i);
mFontMetrics.AppendElement(fm);
}
fm->GetThebesFontGroup()->UpdateUserFonts();
return do_AddRef(fm);
}
}
// It's not in the cache. Get font metrics and then cache them.
nsFontMetrics::Params params = aParams;
params.language = language;
RefPtr<nsFontMetrics> fm = new nsFontMetrics(aFont, params, mContext);
// the mFontMetrics list has the "head" at the end, because append
// is cheaper than insert
mFontMetrics.AppendElement(do_AddRef(fm.get()).take());
return fm.forget();
}
void
nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
{
mFontMetrics.RemoveElement(aFontMetrics);
}
void
nsFontCache::Compact()
{
// Need to loop backward because the running element can be removed on
// the way
for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
nsFontMetrics* fm = mFontMetrics[i];
nsFontMetrics* oldfm = fm;
// Destroy() isn't here because we want our device context to be
// notified
NS_RELEASE(fm); // this will reset fm to nullptr
// if the font is really gone, it would have called back in
// FontMetricsDeleted() and would have removed itself
if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
// nope, the font is still there, so let's hold onto it too
NS_ADDREF(oldfm);
}
}
}
void
nsFontCache::Flush()
{
for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
nsFontMetrics* fm = mFontMetrics[i];
// Destroy() will unhook our device context from the fm so that we
// won't waste time in triggering the notification of
// FontMetricsDeleted() in the subsequent release
fm->Destroy();
NS_RELEASE(fm);
}
mFontMetrics.Clear();
}
nsDeviceContext::nsDeviceContext()
: mWidth(0), mHeight(0), mDepth(0),
mAppUnitsPerDevPixel(-1), mAppUnitsPerDevPixelAtUnitFullZoom(-1),
mAppUnitsPerPhysicalInch(-1),
mFullZoom(1.0f), mPrintingScale(1.0f)
#ifdef DEBUG
, mIsInitialized(false)
#endif
{
MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
}
nsDeviceContext::~nsDeviceContext()
{
if (mFontCache) {
mFontCache->Destroy();
}
}
already_AddRefed<nsFontMetrics>
nsDeviceContext::GetMetricsFor(const nsFont& aFont,
const nsFontMetrics::Params& aParams)
{
if (!mFontCache) {
mFontCache = new nsFontCache();
mFontCache->Init(this);
}
return mFontCache->GetMetricsFor(aFont, aParams);
}
nsresult
nsDeviceContext::FlushFontCache(void)
{
if (mFontCache)
mFontCache->Flush();
return NS_OK;
}
nsresult
nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
{
if (mFontCache) {
mFontCache->FontMetricsDeleted(aFontMetrics);
}
return NS_OK;
}
bool
nsDeviceContext::IsPrinterContext()
{
return mPrintTarget != nullptr
#ifdef XP_MACOSX
|| mCachedPrintTarget != nullptr
#endif
;
}
void
nsDeviceContext::SetDPI(double* aScale)
{
float dpi = -1.0f;
// Use the printing DC to determine DPI values, if we have one.
if (mDeviceContextSpec) {
dpi = mDeviceContextSpec->GetDPI();
mPrintingScale = mDeviceContextSpec->GetPrintingScale();
mAppUnitsPerDevPixelAtUnitFullZoom =
NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
} else {
// A value of -1 means use the maximum of 96 and the system DPI.
// A value of 0 means use the system DPI. A positive value is used as the DPI.
// This sets the physical size of a device pixel and thus controls the
// interpretation of physical units.
int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
if (prefDPI > 0) {
dpi = prefDPI;
} else if (mWidget) {
dpi = mWidget->GetDPI();
if (prefDPI < 0) {
dpi = std::max(96.0f, dpi);
}
} else {
dpi = 96.0f;
}
double devPixelsPerCSSPixel;
if (aScale && *aScale > 0.0) {
// if caller provided a scale, we just use it
devPixelsPerCSSPixel = *aScale;
} else {
// otherwise get from the widget, and return it in aScale for
// the caller to pass to child contexts if needed
CSSToLayoutDeviceScale scale =
mWidget ? mWidget->GetDefaultScale()
: CSSToLayoutDeviceScale(1.0);
devPixelsPerCSSPixel = scale.scale;
if (aScale) {
*aScale = devPixelsPerCSSPixel;
}
}
mAppUnitsPerDevPixelAtUnitFullZoom =
std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
}
NS_ASSERTION(dpi != -1.0, "no dpi set");
mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
UpdateAppUnitsForFullZoom();
}
nsresult
nsDeviceContext::Init(nsIWidget *aWidget)
{
#ifdef DEBUG
// We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
// re-initializes nsDeviceContext objects. We can only assert in
// InitForPrinting (below).
mIsInitialized = true;
#endif
nsresult rv = NS_OK;
if (mScreenManager && mWidget == aWidget)
return rv;
mWidget = aWidget;
SetDPI();
if (mScreenManager)
return rv;
mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
return rv;
}
// XXX This is only for printing. We should make that obvious in the name.
already_AddRefed<gfxContext>
nsDeviceContext::CreateRenderingContext()
{
return CreateRenderingContextCommon(/* not a reference context */ false);
}
already_AddRefed<gfxContext>
nsDeviceContext::CreateReferenceRenderingContext()
{
return CreateRenderingContextCommon(/* a reference context */ true);
}
already_AddRefed<gfxContext>
nsDeviceContext::CreateRenderingContextCommon(bool aWantReferenceContext)
{
MOZ_ASSERT(IsPrinterContext());
MOZ_ASSERT(mWidth > 0 && mHeight > 0);
RefPtr<PrintTarget> printingTarget = mPrintTarget;
#ifdef XP_MACOSX
// CreateRenderingContext() can be called (on reflow) after EndPage()
// but before BeginPage(). On OS X (and only there) mPrintTarget
// will in this case be null, because OS X printing surfaces are
// per-page, and therefore only truly valid between calls to BeginPage()
// and EndPage(). But we can get away with fudging things here, if need
// be, by using a cached copy.
if (!printingTarget) {
printingTarget = mCachedPrintTarget;
}
#endif
// This will usually be null, depending on the pref print.print_via_parent.
RefPtr<DrawEventRecorder> recorder;
mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
RefPtr<gfx::DrawTarget> dt;
if (aWantReferenceContext) {
dt = printingTarget->GetReferenceDrawTarget(recorder);
} else {
dt = printingTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
}
if (!dt || !dt->IsValid()) {
gfxCriticalNote
<< "Failed to create draw target in device context sized "
<< mWidth << "x" << mHeight << " and pointers "
<< hexa(mPrintTarget) << " and " << hexa(printingTarget);
return nullptr;
}
#ifdef XP_MACOSX
dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
#endif
dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
MOZ_ASSERT(pContext); // already checked draw target above
gfxMatrix transform;
if (printingTarget->RotateNeededForLandscape()) {
// Rotate page 90 degrees to draw landscape page on portrait paper
IntSize size = printingTarget->GetSize();
transform.Translate(gfxPoint(0, size.width));
gfxMatrix rotate(0, -1,
1, 0,
0, 0);
transform = rotate * transform;
}
transform.Scale(mPrintingScale, mPrintingScale);
pContext->SetMatrix(transform);
return pContext.forget();
}
nsresult
nsDeviceContext::GetDepth(uint32_t& aDepth)
{
if (mDepth == 0 && mScreenManager) {
nsCOMPtr<nsIScreen> primaryScreen;
mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
primaryScreen->GetColorDepth(reinterpret_cast<int32_t *>(&mDepth));
}
aDepth = mDepth;
return NS_OK;
}
nsresult
nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
{
if (IsPrinterContext()) {
aWidth = mWidth;
aHeight = mHeight;
} else {
nsRect area;
ComputeFullAreaUsingScreen(&area);
aWidth = area.width;
aHeight = area.height;
}
return NS_OK;
}
nsresult
nsDeviceContext::GetRect(nsRect &aRect)
{
if (IsPrinterContext()) {
aRect.x = 0;
aRect.y = 0;
aRect.width = mWidth;
aRect.height = mHeight;
} else
ComputeFullAreaUsingScreen ( &aRect );
return NS_OK;
}
nsresult
nsDeviceContext::GetClientRect(nsRect &aRect)
{
if (IsPrinterContext()) {
aRect.x = 0;
aRect.y = 0;
aRect.width = mWidth;
aRect.height = mHeight;
}
else
ComputeClientRectUsingScreen(&aRect);
return NS_OK;
}
nsresult
nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
{
NS_ENSURE_ARG_POINTER(aDevice);
MOZ_ASSERT(!mIsInitialized,
"Only initialize once, immediately after construction");
// We don't set mIsInitialized here. The Init() call below does that.
mPrintTarget = aDevice->MakePrintTarget();
if (!mPrintTarget) {
return NS_ERROR_FAILURE;
}
mDeviceContextSpec = aDevice;
Init(nullptr);
if (!CalcPrintingSize()) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
nsDeviceContext::BeginDocument(const nsAString& aTitle,
const nsAString& aPrintToFileName,
int32_t aStartPage,
int32_t aEndPage)
{
nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName);
if (NS_SUCCEEDED(rv) && mDeviceContextSpec) {
rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
aStartPage, aEndPage);
}
return rv;
}
nsresult
nsDeviceContext::EndDocument(void)
{
nsresult rv = NS_OK;
if (mPrintTarget) {
rv = mPrintTarget->EndPrinting();
if (NS_SUCCEEDED(rv))
mPrintTarget->Finish();
}
if (mDeviceContextSpec)
mDeviceContextSpec->EndDocument();
return rv;
}
nsresult
nsDeviceContext::AbortDocument(void)
{
nsresult rv = mPrintTarget->AbortPrinting();
if (mDeviceContextSpec)
mDeviceContextSpec->EndDocument();
return rv;
}
nsresult
nsDeviceContext::BeginPage(void)
{
nsresult rv = NS_OK;
if (mDeviceContextSpec)
rv = mDeviceContextSpec->BeginPage();
if (NS_FAILED(rv)) return rv;
#ifdef XP_MACOSX
// We need to get a new surface for each page on the Mac, as the
// CGContextRefs are only good for one page.
mPrintTarget = mDeviceContextSpec->MakePrintTarget();
#endif
rv = mPrintTarget->BeginPage();
return rv;
}
nsresult
nsDeviceContext::EndPage(void)
{
nsresult rv = mPrintTarget->EndPage();
#ifdef XP_MACOSX
// We need to release the CGContextRef in the surface here, plus it's
// not something you would want anyway, as these CGContextRefs are only
// good for one page. But we need to keep a cached reference to it, since
// CreateRenderingContext() may try to access it when mPrintTarget
// would normally be null. See bug 665218. If we just stop nulling out
// mPrintTarget here (and thereby make that our cached copy), we'll
// break all our null checks on mPrintTarget. See bug 684622.
mCachedPrintTarget = mPrintTarget;
mPrintTarget = nullptr;
#endif
if (mDeviceContextSpec)
mDeviceContextSpec->EndPage();
return rv;
}
void
nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect)
{
// we always need to recompute the clientRect
// because the window may have moved onto a different screen. In the single
// monitor case, we only need to do the computation if we haven't done it
// once already, and remember that we have because we're assured it won't change.
nsCOMPtr<nsIScreen> screen;
FindScreen (getter_AddRefs(screen));
if (screen) {
int32_t x, y, width, height;
screen->GetAvailRect(&x, &y, &width, &height);
// convert to device units
outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
}
}
void
nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect)
{
// if we have more than one screen, we always need to recompute the clientRect
// because the window may have moved onto a different screen. In the single
// monitor case, we only need to do the computation if we haven't done it
// once already, and remember that we have because we're assured it won't change.
nsCOMPtr<nsIScreen> screen;
FindScreen ( getter_AddRefs(screen) );
if ( screen ) {
int32_t x, y, width, height;
screen->GetRect ( &x, &y, &width, &height );
// convert to device units
outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
mWidth = outRect->width;
mHeight = outRect->height;
}
}
//
// FindScreen
//
// Determines which screen intersects the largest area of the given surface.
//
void
nsDeviceContext::FindScreen(nsIScreen** outScreen)
{
if (!mWidget || !mScreenManager) {
return;
}
CheckDPIChange();
if (mWidget->GetOwningTabChild()) {
mScreenManager->ScreenForNativeWidget((void *)mWidget->GetOwningTabChild(),
outScreen);
}
else if (mWidget->GetNativeData(NS_NATIVE_WINDOW)) {
mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW),
outScreen);
}
#ifdef MOZ_WIDGET_ANDROID
if (!(*outScreen)) {
nsCOMPtr<nsIScreen> screen = mWidget->GetWidgetScreen();
screen.forget(outScreen);
}
#endif
if (!(*outScreen)) {
mScreenManager->GetPrimaryScreen(outScreen);
}
}
bool
nsDeviceContext::CalcPrintingSize()
{
if (!mPrintTarget) {
return (mWidth > 0 && mHeight > 0);
}
gfxSize size = mPrintTarget->GetSize();
// For printing, CSS inches and physical inches are identical
// so it doesn't matter which we use here
mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch()
/ POINTS_PER_INCH_FLOAT);
mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch()
/ POINTS_PER_INCH_FLOAT);
return (mWidth > 0 && mHeight > 0);
}
bool nsDeviceContext::CheckDPIChange(double* aScale)
{
int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
int32_t oldInches = mAppUnitsPerPhysicalInch;
SetDPI(aScale);
return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
oldInches != mAppUnitsPerPhysicalInch;
}
bool
nsDeviceContext::SetFullZoom(float aScale)
{
if (aScale <= 0) {
NS_NOTREACHED("Invalid full zoom value");
return false;
}
int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
mFullZoom = aScale;
UpdateAppUnitsForFullZoom();
return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
}
void
nsDeviceContext::UpdateAppUnitsForFullZoom()
{
mAppUnitsPerDevPixel =
std::max(1, NSToIntRound(float(mAppUnitsPerDevPixelAtUnitFullZoom) / mFullZoom));
// adjust mFullZoom to reflect appunit rounding
mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
}
DesktopToLayoutDeviceScale
nsDeviceContext::GetDesktopToDeviceScale()
{
nsCOMPtr<nsIScreen> screen;
FindScreen(getter_AddRefs(screen));
if (screen) {
double scale;
screen->GetContentsScaleFactor(&scale);
return DesktopToLayoutDeviceScale(scale);
}
return DesktopToLayoutDeviceScale(1.0);
}

311
gfx/src/nsDeviceContext.h Normal file
View file

@ -0,0 +1,311 @@
/* -*- Mode: C++; 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/. */
#ifndef _NS_DEVICECONTEXT_H_
#define _NS_DEVICECONTEXT_H_
#include <stdint.h> // for uint32_t
#include <sys/types.h> // for int32_t
#include "gfxTypes.h" // for gfxFloat
#include "gfxFont.h" // for gfxFont::Orientation
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/RefPtr.h" // for RefPtr
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsCoord.h" // for nscoord
#include "nsError.h" // for nsresult
#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
#include "nsMathUtils.h" // for NS_round
#include "nscore.h" // for char16_t, nsAString
#include "mozilla/AppUnits.h" // for AppUnits
#include "nsFontMetrics.h" // for nsFontMetrics::Params
class gfxContext;
class gfxTextPerfMetrics;
class gfxUserFontSet;
struct nsFont;
class nsFontCache;
class nsIAtom;
class nsIDeviceContextSpec;
class nsIScreen;
class nsIScreenManager;
class nsIWidget;
struct nsRect;
namespace mozilla {
namespace gfx {
class PrintTarget;
}
}
class nsDeviceContext final
{
public:
typedef mozilla::gfx::PrintTarget PrintTarget;
nsDeviceContext();
NS_INLINE_DECL_REFCOUNTING(nsDeviceContext)
/**
* Initialize the device context from a widget
* @param aWidget a widget to initialize the device context from
* @return error status
*/
nsresult Init(nsIWidget *aWidget);
/**
* Initialize the device context from a device context spec
* @param aDevSpec the specification of the printing device
* @return error status
*/
nsresult InitForPrinting(nsIDeviceContextSpec *aDevSpec);
/**
* Create a rendering context and initialize it. Only call this
* method on device contexts that were initialized for printing.
*
* @return the new rendering context (guaranteed to be non-null)
*/
already_AddRefed<gfxContext> CreateRenderingContext();
/**
* Create a reference rendering context and initialize it. Only call this
* method on device contexts that were initialized for printing.
*
* @return the new rendering context.
*/
already_AddRefed<gfxContext> CreateReferenceRenderingContext();
/**
* Gets the number of app units in one CSS pixel; this number is global,
* not unique to each device context.
*/
static int32_t AppUnitsPerCSSPixel() { return mozilla::AppUnitsPerCSSPixel(); }
/**
* Gets the number of app units in one device pixel; this number
* is usually a factor of AppUnitsPerCSSPixel(), although that is
* not guaranteed.
*/
int32_t AppUnitsPerDevPixel() const { return mAppUnitsPerDevPixel; }
/**
* Convert device pixels which is used for gfx/thebes to nearest
* (rounded) app units
*/
nscoord GfxUnitsToAppUnits(gfxFloat aGfxUnits) const
{ return nscoord(NS_round(aGfxUnits * AppUnitsPerDevPixel())); }
/**
* Convert app units to device pixels which is used for gfx/thebes.
*/
gfxFloat AppUnitsToGfxUnits(nscoord aAppUnits) const
{ return gfxFloat(aAppUnits) / AppUnitsPerDevPixel(); }
/**
* Gets the number of app units in one physical inch; this is the
* device's DPI times AppUnitsPerDevPixel().
*/
int32_t AppUnitsPerPhysicalInch() const
{ return mAppUnitsPerPhysicalInch; }
/**
* Gets the number of app units in one CSS inch; this is
* 96 times AppUnitsPerCSSPixel.
*/
static int32_t AppUnitsPerCSSInch() { return mozilla::AppUnitsPerCSSInch(); }
/**
* Get the ratio of app units to dev pixels that would be used at unit
* (100%) full zoom.
*/
int32_t AppUnitsPerDevPixelAtUnitFullZoom() const
{ return mAppUnitsPerDevPixelAtUnitFullZoom; }
/**
* Get the nsFontMetrics that describe the properties of
* an nsFont.
* @param aFont font description to obtain metrics for
*/
already_AddRefed<nsFontMetrics> GetMetricsFor(
const nsFont& aFont, const nsFontMetrics::Params& aParams);
/**
* Notification when a font metrics instance created for this device is
* about to be deleted
*/
nsresult FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
/**
* Attempt to free up resources by flushing out any fonts no longer
* referenced by anything other than the font cache itself.
* @return error status
*/
nsresult FlushFontCache();
/**
* Return the bit depth of the device.
*/
nsresult GetDepth(uint32_t& aDepth);
/**
* Get the size of the displayable area of the output device
* in app units.
* @param aWidth out parameter for width
* @param aHeight out parameter for height
* @return error status
*/
nsresult GetDeviceSurfaceDimensions(nscoord& aWidth, nscoord& aHeight);
/**
* Get the size of the content area of the output device in app
* units. This corresponds on a screen device, for instance, to
* the entire screen.
* @param aRect out parameter for full rect. Position (x,y) will
* be (0,0) or relative to the primary monitor if
* this is not the primary.
* @return error status
*/
nsresult GetRect(nsRect& aRect);
/**
* Get the size of the content area of the output device in app
* units. This corresponds on a screen device, for instance, to
* the area reported by GetDeviceSurfaceDimensions, minus the
* taskbar (Windows) or menubar (Macintosh).
* @param aRect out parameter for client rect. Position (x,y) will
* be (0,0) adjusted for any upper/left non-client
* space if present or relative to the primary
* monitor if this is not the primary.
* @return error status
*/
nsresult GetClientRect(nsRect& aRect);
/**
* Inform the output device that output of a document is beginning
* Used for print related device contexts. Must be matched 1:1 with
* EndDocument() or AbortDocument().
*
* @param aTitle - title of Document
* @param aPrintToFileName - name of file to print to, if empty then don't
* print to file
* @param aStartPage - starting page number (must be greater than zero)
* @param aEndPage - ending page number (must be less than or
* equal to number of pages)
*
* @return error status
*/
nsresult BeginDocument(const nsAString& aTitle,
const nsAString& aPrintToFileName,
int32_t aStartPage,
int32_t aEndPage);
/**
* Inform the output device that output of a document is ending.
* Used for print related device contexts. Must be matched 1:1 with
* BeginDocument()
* @return error status
*/
nsresult EndDocument();
/**
* Inform the output device that output of a document is being aborted.
* Must be matched 1:1 with BeginDocument()
* @return error status
*/
nsresult AbortDocument();
/**
* Inform the output device that output of a page is beginning
* Used for print related device contexts. Must be matched 1:1 with
* EndPage() and within a BeginDocument()/EndDocument() pair.
* @return error status
*/
nsresult BeginPage();
/**
* Inform the output device that output of a page is ending
* Used for print related device contexts. Must be matched 1:1 with
* BeginPage() and within a BeginDocument()/EndDocument() pair.
* @return error status
*/
nsresult EndPage();
/**
* Check to see if the DPI has changed, or impose a new DPI scale value.
* @param aScale - If non-null, the default (unzoomed) CSS to device pixel
* scale factor will be returned here; and if it is > 0.0
* on input, the given value will be used instead of
* getting it from the widget (if any). This is used to
* allow subdocument contexts to inherit the resolution
* setting of their parent.
* @return whether there was actually a change in the DPI (whether
* AppUnitsPerDevPixel() or AppUnitsPerPhysicalInch()
* changed)
*/
bool CheckDPIChange(double* aScale = nullptr);
/**
* Set the full zoom factor: all lengths are multiplied by this factor
* when we convert them to device pixels. Returns whether the ratio of
* app units to dev pixels changed because of the zoom factor.
*/
bool SetFullZoom(float aScale);
/**
* Returns the page full zoom factor applied.
*/
float GetFullZoom() const { return mFullZoom; }
/**
* True if this device context was created for printing.
*/
bool IsPrinterContext();
mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale();
private:
// Private destructor, to discourage deletion outside of Release():
~nsDeviceContext();
/**
* Implementation shared by CreateRenderingContext and
* CreateReferenceRenderingContext.
*/
already_AddRefed<gfxContext>
CreateRenderingContextCommon(bool aWantReferenceContext);
void SetDPI(double* aScale = nullptr);
void ComputeClientRectUsingScreen(nsRect *outRect);
void ComputeFullAreaUsingScreen(nsRect *outRect);
void FindScreen(nsIScreen **outScreen);
// Return false if the surface is not right
bool CalcPrintingSize();
void UpdateAppUnitsForFullZoom();
nscoord mWidth;
nscoord mHeight;
uint32_t mDepth;
int32_t mAppUnitsPerDevPixel;
int32_t mAppUnitsPerDevPixelAtUnitFullZoom;
int32_t mAppUnitsPerPhysicalInch;
float mFullZoom;
float mPrintingScale;
RefPtr<nsFontCache> mFontCache;
nsCOMPtr<nsIWidget> mWidget;
nsCOMPtr<nsIScreenManager> mScreenManager;
nsCOMPtr<nsIDeviceContextSpec> mDeviceContextSpec;
RefPtr<PrintTarget> mPrintTarget;
#ifdef XP_MACOSX
RefPtr<PrintTarget> mCachedPrintTarget;
#endif
#ifdef DEBUG
bool mIsInitialized;
#endif
};
#endif /* _NS_DEVICECONTEXT_H_ */

297
gfx/src/nsFont.cpp Normal file
View file

@ -0,0 +1,297 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsFont.h"
#include "gfxFont.h" // for gfxFontStyle
#include "gfxFontConstants.h" // for NS_FONT_KERNING_AUTO, etc
#include "gfxFontFeatures.h" // for gfxFontFeature, etc
#include "gfxFontUtils.h" // for TRUETYPE_TAG
#include "nsCRT.h" // for nsCRT
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupports.h"
#include "nsUnicharUtils.h"
#include "nscore.h" // for char16_t
#include "mozilla/ArrayUtils.h"
#include "mozilla/gfx/2D.h"
using namespace mozilla;
nsFont::nsFont(const FontFamilyList& aFontlist, nscoord aSize)
: fontlist(aFontlist)
{
Init();
size = aSize;
}
nsFont::nsFont(FontFamilyType aGenericType, nscoord aSize)
: fontlist(aGenericType)
{
Init();
size = aSize;
}
void
nsFont::Init()
{
style = NS_FONT_STYLE_NORMAL;
weight = NS_FONT_WEIGHT_NORMAL;
stretch = NS_FONT_STRETCH_NORMAL;
systemFont = false;
smoothing = NS_FONT_SMOOTHING_AUTO;
sizeAdjust = -1.0f;
kerning = NS_FONT_KERNING_AUTO;
synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE;
variantAlternates = 0;
variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
variantEastAsian = 0;
variantLigatures = 0;
variantNumeric = 0;
variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
}
nsFont::nsFont(const nsFont& aOther) = default;
nsFont::nsFont()
{
}
nsFont::~nsFont()
{
}
bool nsFont::Equals(const nsFont& aOther) const
{
if ((style == aOther.style) &&
(systemFont == aOther.systemFont) &&
(weight == aOther.weight) &&
(stretch == aOther.stretch) &&
(size == aOther.size) &&
(sizeAdjust == aOther.sizeAdjust) &&
(fontlist == aOther.fontlist) &&
(kerning == aOther.kerning) &&
(synthesis == aOther.synthesis) &&
(fontFeatureSettings == aOther.fontFeatureSettings) &&
(languageOverride == aOther.languageOverride) &&
(variantAlternates == aOther.variantAlternates) &&
(variantCaps == aOther.variantCaps) &&
(variantEastAsian == aOther.variantEastAsian) &&
(variantLigatures == aOther.variantLigatures) &&
(variantNumeric == aOther.variantNumeric) &&
(variantPosition == aOther.variantPosition) &&
(variantWidth == aOther.variantWidth) &&
(alternateValues == aOther.alternateValues) &&
(featureValueLookup == aOther.featureValueLookup) &&
(smoothing == aOther.smoothing)) {
return true;
}
return false;
}
nsFont& nsFont::operator=(const nsFont& aOther) = default;
void
nsFont::CopyAlternates(const nsFont& aOther)
{
variantAlternates = aOther.variantAlternates;
alternateValues = aOther.alternateValues;
featureValueLookup = aOther.featureValueLookup;
}
// mapping from bitflag to font feature tag/value pair
//
// these need to be kept in sync with the constants listed
// in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78)
// NS_FONT_VARIANT_EAST_ASIAN_xxx values
const gfxFontFeature eastAsianDefaults[] = {
{ TRUETYPE_TAG('j','p','7','8'), 1 },
{ TRUETYPE_TAG('j','p','8','3'), 1 },
{ TRUETYPE_TAG('j','p','9','0'), 1 },
{ TRUETYPE_TAG('j','p','0','4'), 1 },
{ TRUETYPE_TAG('s','m','p','l'), 1 },
{ TRUETYPE_TAG('t','r','a','d'), 1 },
{ TRUETYPE_TAG('f','w','i','d'), 1 },
{ TRUETYPE_TAG('p','w','i','d'), 1 },
{ TRUETYPE_TAG('r','u','b','y'), 1 }
};
static_assert(MOZ_ARRAY_LENGTH(eastAsianDefaults) ==
eFeatureEastAsian_numFeatures,
"eFeatureEastAsian_numFeatures should be correct");
// NS_FONT_VARIANT_LIGATURES_xxx values
const gfxFontFeature ligDefaults[] = {
{ TRUETYPE_TAG('l','i','g','a'), 0 }, // none value means all off
{ TRUETYPE_TAG('l','i','g','a'), 1 },
{ TRUETYPE_TAG('l','i','g','a'), 0 },
{ TRUETYPE_TAG('d','l','i','g'), 1 },
{ TRUETYPE_TAG('d','l','i','g'), 0 },
{ TRUETYPE_TAG('h','l','i','g'), 1 },
{ TRUETYPE_TAG('h','l','i','g'), 0 },
{ TRUETYPE_TAG('c','a','l','t'), 1 },
{ TRUETYPE_TAG('c','a','l','t'), 0 }
};
static_assert(MOZ_ARRAY_LENGTH(ligDefaults) ==
eFeatureLigatures_numFeatures,
"eFeatureLigatures_numFeatures should be correct");
// NS_FONT_VARIANT_NUMERIC_xxx values
const gfxFontFeature numericDefaults[] = {
{ TRUETYPE_TAG('l','n','u','m'), 1 },
{ TRUETYPE_TAG('o','n','u','m'), 1 },
{ TRUETYPE_TAG('p','n','u','m'), 1 },
{ TRUETYPE_TAG('t','n','u','m'), 1 },
{ TRUETYPE_TAG('f','r','a','c'), 1 },
{ TRUETYPE_TAG('a','f','r','c'), 1 },
{ TRUETYPE_TAG('z','e','r','o'), 1 },
{ TRUETYPE_TAG('o','r','d','n'), 1 }
};
static_assert(MOZ_ARRAY_LENGTH(numericDefaults) ==
eFeatureNumeric_numFeatures,
"eFeatureNumeric_numFeatures should be correct");
static void
AddFontFeaturesBitmask(uint32_t aValue, uint32_t aMin, uint32_t aMax,
const gfxFontFeature aFeatureDefaults[],
nsTArray<gfxFontFeature>& aFeaturesOut)
{
uint32_t i, m;
for (i = 0, m = aMin; m <= aMax; i++, m <<= 1) {
if (m & aValue) {
const gfxFontFeature& feature = aFeatureDefaults[i];
aFeaturesOut.AppendElement(feature);
}
}
}
static uint32_t
FontFeatureTagForVariantWidth(uint32_t aVariantWidth)
{
switch (aVariantWidth) {
case NS_FONT_VARIANT_WIDTH_FULL:
return TRUETYPE_TAG('f','w','i','d');
case NS_FONT_VARIANT_WIDTH_HALF:
return TRUETYPE_TAG('h','w','i','d');
case NS_FONT_VARIANT_WIDTH_THIRD:
return TRUETYPE_TAG('t','w','i','d');
case NS_FONT_VARIANT_WIDTH_QUARTER:
return TRUETYPE_TAG('q','w','i','d');
default:
return 0;
}
}
void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const
{
// add in font-variant features
gfxFontFeature setting;
// -- kerning
setting.mTag = TRUETYPE_TAG('k','e','r','n');
switch (kerning) {
case NS_FONT_KERNING_NONE:
setting.mValue = 0;
aStyle->featureSettings.AppendElement(setting);
break;
case NS_FONT_KERNING_NORMAL:
setting.mValue = 1;
aStyle->featureSettings.AppendElement(setting);
break;
default:
// auto case implies use user agent default
break;
}
// -- alternates
if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_HISTORICAL) {
setting.mValue = 1;
setting.mTag = TRUETYPE_TAG('h','i','s','t');
aStyle->featureSettings.AppendElement(setting);
}
// -- copy font-specific alternate info into style
// (this will be resolved after font-matching occurs)
aStyle->alternateValues.AppendElements(alternateValues);
aStyle->featureValueLookup = featureValueLookup;
// -- caps
aStyle->variantCaps = variantCaps;
// -- east-asian
if (variantEastAsian) {
AddFontFeaturesBitmask(variantEastAsian,
NS_FONT_VARIANT_EAST_ASIAN_JIS78,
NS_FONT_VARIANT_EAST_ASIAN_RUBY,
eastAsianDefaults, aStyle->featureSettings);
}
// -- ligatures
if (variantLigatures) {
AddFontFeaturesBitmask(variantLigatures,
NS_FONT_VARIANT_LIGATURES_NONE,
NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL,
ligDefaults, aStyle->featureSettings);
if (variantLigatures & NS_FONT_VARIANT_LIGATURES_COMMON) {
// liga already enabled, need to enable clig also
setting.mTag = TRUETYPE_TAG('c','l','i','g');
setting.mValue = 1;
aStyle->featureSettings.AppendElement(setting);
} else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NO_COMMON) {
// liga already disabled, need to disable clig also
setting.mTag = TRUETYPE_TAG('c','l','i','g');
setting.mValue = 0;
aStyle->featureSettings.AppendElement(setting);
} else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NONE) {
// liga already disabled, need to disable dlig, hlig, calt, clig
setting.mValue = 0;
setting.mTag = TRUETYPE_TAG('d','l','i','g');
aStyle->featureSettings.AppendElement(setting);
setting.mTag = TRUETYPE_TAG('h','l','i','g');
aStyle->featureSettings.AppendElement(setting);
setting.mTag = TRUETYPE_TAG('c','a','l','t');
aStyle->featureSettings.AppendElement(setting);
setting.mTag = TRUETYPE_TAG('c','l','i','g');
aStyle->featureSettings.AppendElement(setting);
}
}
// -- numeric
if (variantNumeric) {
AddFontFeaturesBitmask(variantNumeric,
NS_FONT_VARIANT_NUMERIC_LINING,
NS_FONT_VARIANT_NUMERIC_ORDINAL,
numericDefaults, aStyle->featureSettings);
}
// -- position
aStyle->variantSubSuper = variantPosition;
// -- width
setting.mTag = FontFeatureTagForVariantWidth(variantWidth);
if (setting.mTag) {
setting.mValue = 1;
aStyle->featureSettings.AppendElement(setting);
}
// indicate common-path case when neither variantCaps or variantSubSuper are set
aStyle->noFallbackVariantFeatures =
(aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL) &&
(variantPosition == NS_FONT_VARIANT_POSITION_NORMAL);
// add in features from font-feature-settings
aStyle->featureSettings.AppendElements(fontFeatureSettings);
// enable grayscale antialiasing for text
if (smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
aStyle->useGrayscaleAntialiasing = true;
}
}

147
gfx/src/nsFont.h Normal file
View file

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef nsFont_h___
#define nsFont_h___
#include <stdint.h> // for uint8_t, uint16_t
#include <sys/types.h> // for int16_t
#include "gfxFontFamilyList.h"
#include "gfxFontFeatures.h"
#include "mozilla/RefPtr.h" // for RefPtr
#include "nsCoord.h" // for nscoord
#include "nsStringFwd.h" // for nsSubstring
#include "nsString.h" // for nsString
#include "nsTArray.h" // for nsTArray
struct gfxFontStyle;
// XXX we need a method to enumerate all of the possible fonts on the
// system across family, weight, style, size, etc. But not here!
// Enumerator callback function. Return false to stop
typedef bool (*nsFontFamilyEnumFunc)(const nsString& aFamily, bool aGeneric, void *aData);
// IDs for generic fonts
// NOTE: 0, 1 are reserved for the special IDs of the default variable
// and fixed fonts in the presentation context, see nsPresContext.h
const uint8_t kGenericFont_NONE = 0x00;
// Special
const uint8_t kGenericFont_moz_variable = 0x00; // for the default variable width font
const uint8_t kGenericFont_moz_fixed = 0x01; // our special "use the user's fixed font"
// CSS
const uint8_t kGenericFont_serif = 0x02;
const uint8_t kGenericFont_sans_serif = 0x04;
const uint8_t kGenericFont_monospace = 0x08;
const uint8_t kGenericFont_cursive = 0x10;
const uint8_t kGenericFont_fantasy = 0x20;
// Font structure.
struct nsFont {
// list of font families, either named or generic
mozilla::FontFamilyList fontlist;
// The style of font (normal, italic, oblique; see gfxFontConstants.h)
uint8_t style;
// Force this font to not be considered a 'generic' font, even if
// the name is the same as a CSS generic font family.
bool systemFont;
// Variant subproperties
uint8_t variantCaps;
uint8_t variantNumeric;
uint8_t variantPosition;
uint8_t variantWidth;
uint16_t variantLigatures;
uint16_t variantEastAsian;
// Some font-variant-alternates property values require
// font-specific settings defined via @font-feature-values rules.
// These are resolved *after* font matching occurs.
// -- bitmask for both enumerated and functional propvals
uint16_t variantAlternates;
// Smoothing - controls subpixel-antialiasing (currently OSX only)
uint8_t smoothing;
// The weight of the font; see gfxFontConstants.h.
uint16_t weight;
// The stretch of the font (the sum of various NS_FONT_STRETCH_*
// constants; see gfxFontConstants.h).
int16_t stretch;
// Kerning
uint8_t kerning;
// Synthesis setting, controls use of fake bolding/italics
uint8_t synthesis;
// The logical size of the font, in nscoord units
nscoord size;
// The aspect-value (ie., the ratio actualsize:actualxheight) that any
// actual physical font created from this font structure must have when
// rendering or measuring a string. A value of -1.0 means no adjustment
// needs to be done; otherwise the value must be nonnegative.
float sizeAdjust;
// -- list of value tags for font-specific alternate features
nsTArray<gfxAlternateValue> alternateValues;
// -- object used to look these up once the font is matched
RefPtr<gfxFontFeatureValueSet> featureValueLookup;
// Font features from CSS font-feature-settings
nsTArray<gfxFontFeature> fontFeatureSettings;
// Language system tag, to override document language;
// this is an OpenType "language system" tag represented as a 32-bit integer
// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
nsString languageOverride;
// initialize the font with a fontlist
nsFont(const mozilla::FontFamilyList& aFontlist, nscoord aSize);
// initialize the font with a single generic
nsFont(mozilla::FontFamilyType aGenericType, nscoord aSize);
// Make a copy of the given font
nsFont(const nsFont& aFont);
// leave members uninitialized
nsFont();
~nsFont();
bool operator==(const nsFont& aOther) const {
return Equals(aOther);
}
bool operator!=(const nsFont& aOther) const {
return !Equals(aOther);
}
bool Equals(const nsFont& aOther) const;
nsFont& operator=(const nsFont& aOther);
void CopyAlternates(const nsFont& aOther);
// Add featureSettings into style
void AddFontFeaturesToStyle(gfxFontStyle *aStyle) const;
protected:
void Init(); // helper method for initialization
};
#define NS_FONT_VARIANT_NORMAL 0
#define NS_FONT_VARIANT_SMALL_CAPS 1
#endif /* nsFont_h___ */

445
gfx/src/nsFontMetrics.cpp Normal file
View file

@ -0,0 +1,445 @@
/* -*- 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 "nsFontMetrics.h"
#include <math.h> // for floor, ceil
#include <algorithm> // for max
#include "gfxFontConstants.h" // for NS_FONT_SYNTHESIS_*
#include "gfxPlatform.h" // for gfxPlatform
#include "gfxPoint.h" // for gfxPoint
#include "gfxRect.h" // for gfxRect
#include "gfxTypes.h" // for gfxFloat
#include "nsBoundingMetrics.h" // for nsBoundingMetrics
#include "nsDebug.h" // for NS_ERROR
#include "nsDeviceContext.h" // for nsDeviceContext
#include "nsIAtom.h" // for nsIAtom
#include "nsMathUtils.h" // for NS_round
#include "nsRenderingContext.h" // for nsRenderingContext
#include "nsString.h" // for nsString
#include "nsStyleConsts.h" // for NS_STYLE_HYPHENS_NONE
#include "mozilla/Assertions.h" // for MOZ_ASSERT
#include "mozilla/UniquePtr.h" // for UniquePtr
class gfxUserFontSet;
using namespace mozilla;
namespace {
class AutoTextRun {
public:
typedef mozilla::gfx::DrawTarget DrawTarget;
AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
const char* aString, int32_t aLength)
{
mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
reinterpret_cast<const uint8_t*>(aString), aLength,
aDrawTarget,
aMetrics->AppUnitsPerDevPixel(),
ComputeFlags(aMetrics),
nullptr);
}
AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
const char16_t* aString, int32_t aLength)
{
mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
aString, aLength,
aDrawTarget,
aMetrics->AppUnitsPerDevPixel(),
ComputeFlags(aMetrics),
nullptr);
}
gfxTextRun *get() { return mTextRun.get(); }
gfxTextRun *operator->() { return mTextRun.get(); }
private:
static uint32_t ComputeFlags(nsFontMetrics* aMetrics) {
uint32_t flags = 0;
if (aMetrics->GetTextRunRTL()) {
flags |= gfxTextRunFactory::TEXT_IS_RTL;
}
if (aMetrics->GetVertical()) {
switch (aMetrics->GetTextOrientation()) {
case NS_STYLE_TEXT_ORIENTATION_MIXED:
flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED;
break;
case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
break;
case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
break;
}
}
return flags;
}
RefPtr<gfxTextRun> mTextRun;
};
class StubPropertyProvider : public gfxTextRun::PropertyProvider {
public:
virtual void GetHyphenationBreaks(gfxTextRun::Range aRange,
bool* aBreakBefore) {
NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
}
virtual int8_t GetHyphensOption() {
NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
return NS_STYLE_HYPHENS_NONE;
}
virtual gfxFloat GetHyphenWidth() {
NS_ERROR("This shouldn't be called because we never enable hyphens");
return 0;
}
virtual already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() {
NS_ERROR("This shouldn't be called because we never enable hyphens");
return nullptr;
}
virtual uint32_t GetAppUnitsPerDevUnit() {
NS_ERROR("This shouldn't be called because we never enable hyphens");
return 60;
}
virtual void GetSpacing(gfxTextRun::Range aRange, Spacing* aSpacing) {
NS_ERROR("This shouldn't be called because we never enable spacing");
}
};
} // namespace
nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
nsDeviceContext *aContext)
: mFont(aFont)
, mLanguage(aParams.language)
, mDeviceContext(aContext)
, mP2A(aContext->AppUnitsPerDevPixel())
, mOrientation(aParams.orientation)
, mTextRunRTL(false)
, mVertical(false)
, mTextOrientation(0)
{
gfxFontStyle style(aFont.style,
aFont.weight,
aFont.stretch,
gfxFloat(aFont.size) / mP2A,
aParams.language,
aParams.explicitLanguage,
aFont.sizeAdjust,
aFont.systemFont,
mDeviceContext->IsPrinterContext(),
aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
aFont.languageOverride);
aFont.AddFontFeaturesToStyle(&style);
gfxFloat devToCssSize = gfxFloat(mP2A) /
gfxFloat(mDeviceContext->AppUnitsPerCSSPixel());
mFontGroup = gfxPlatform::GetPlatform()->
CreateFontGroup(aFont.fontlist, &style, aParams.textPerf,
aParams.userFontSet, devToCssSize);
}
nsFontMetrics::~nsFontMetrics()
{
if (mDeviceContext) {
mDeviceContext->FontMetricsDeleted(this);
}
}
void
nsFontMetrics::Destroy()
{
mDeviceContext = nullptr;
}
// XXXTODO get rid of this macro
#define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
#define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
const gfxFont::Metrics&
nsFontMetrics::GetMetrics(gfxFont::Orientation aOrientation) const
{
return mFontGroup->GetFirstValidFont()->GetMetrics(aOrientation);
}
nscoord
nsFontMetrics::XHeight()
{
return ROUND_TO_TWIPS(GetMetrics().xHeight);
}
nscoord
nsFontMetrics::CapHeight()
{
return ROUND_TO_TWIPS(GetMetrics().capHeight);
}
nscoord
nsFontMetrics::SuperscriptOffset()
{
return ROUND_TO_TWIPS(GetMetrics().emHeight *
NS_FONT_SUPERSCRIPT_OFFSET_RATIO);
}
nscoord
nsFontMetrics::SubscriptOffset()
{
return ROUND_TO_TWIPS(GetMetrics().emHeight *
NS_FONT_SUBSCRIPT_OFFSET_RATIO);
}
void
nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize)
{
aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset);
aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize);
}
void
nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
{
aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize);
}
// GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
// text-decoration lines drawable area. See bug 421353.
// BE CAREFUL for rounding each values. The logic MUST be same as
// nsCSSRendering::GetTextDecorationRectInternal's.
static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
gfxFontGroup* aFontGroup)
{
gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
gfxFloat size = NS_round(aMetrics.underlineSize);
gfxFloat minDescent = offset + size;
return floor(std::max(minDescent, aMetrics.maxDescent) + 0.5);
}
static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
{
return floor(aMetrics.maxAscent + 0.5);
}
nscoord
nsFontMetrics::InternalLeading()
{
return ROUND_TO_TWIPS(GetMetrics().internalLeading);
}
nscoord
nsFontMetrics::ExternalLeading()
{
return ROUND_TO_TWIPS(GetMetrics().externalLeading);
}
nscoord
nsFontMetrics::EmHeight()
{
return ROUND_TO_TWIPS(GetMetrics().emHeight);
}
nscoord
nsFontMetrics::EmAscent()
{
return ROUND_TO_TWIPS(GetMetrics().emAscent);
}
nscoord
nsFontMetrics::EmDescent()
{
return ROUND_TO_TWIPS(GetMetrics().emDescent);
}
nscoord
nsFontMetrics::MaxHeight()
{
return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
}
nscoord
nsFontMetrics::MaxAscent()
{
return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
}
nscoord
nsFontMetrics::MaxDescent()
{
return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
}
nscoord
nsFontMetrics::MaxAdvance()
{
return CEIL_TO_TWIPS(GetMetrics().maxAdvance);
}
nscoord
nsFontMetrics::AveCharWidth()
{
// Use CEIL instead of ROUND for consistency with GetMaxAdvance
return CEIL_TO_TWIPS(GetMetrics().aveCharWidth);
}
nscoord
nsFontMetrics::SpaceWidth()
{
// For vertical text with mixed or sideways orientation, we want the
// width of a horizontal space (even if we're using vertical line-spacing
// metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
return CEIL_TO_TWIPS(
GetMetrics(mVertical &&
mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT
? gfxFont::eVertical
: gfxFont::eHorizontal).spaceWidth);
}
int32_t
nsFontMetrics::GetMaxStringLength()
{
const gfxFont::Metrics& m = GetMetrics();
const double x = 32767.0 / m.maxAdvance;
int32_t len = (int32_t)floor(x);
return std::max(1, len);
}
nscoord
nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
DrawTarget* aDrawTarget)
{
if (aLength == 0)
return 0;
if (aLength == 1 && aString[0] == ' ')
return SpaceWidth();
StubPropertyProvider provider;
AutoTextRun textRun(this, aDrawTarget, aString, aLength);
if (textRun.get()) {
return NSToCoordRound(
textRun->GetAdvanceWidth(Range(0, aLength), &provider));
}
return 0;
}
nscoord
nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
DrawTarget* aDrawTarget)
{
if (aLength == 0)
return 0;
if (aLength == 1 && aString[0] == ' ')
return SpaceWidth();
StubPropertyProvider provider;
AutoTextRun textRun(this, aDrawTarget, aString, aLength);
if (textRun.get()) {
return NSToCoordRound(
textRun->GetAdvanceWidth(Range(0, aLength), &provider));
}
return 0;
}
// Draw a string using this font handle on the surface passed in.
void
nsFontMetrics::DrawString(const char *aString, uint32_t aLength,
nscoord aX, nscoord aY,
nsRenderingContext *aContext)
{
if (aLength == 0)
return;
StubPropertyProvider provider;
AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
if (!textRun.get()) {
return;
}
gfxPoint pt(aX, aY);
Range range(0, aLength);
if (mTextRunRTL) {
if (mVertical) {
pt.y += textRun->GetAdvanceWidth(range, &provider);
} else {
pt.x += textRun->GetAdvanceWidth(range, &provider);
}
}
gfxTextRun::DrawParams params(aContext->ThebesContext());
params.provider = &provider;
textRun->Draw(range, pt, params);
}
void
nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
nscoord aX, nscoord aY,
nsRenderingContext *aContext,
DrawTarget* aTextRunConstructionDrawTarget)
{
if (aLength == 0)
return;
StubPropertyProvider provider;
AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
if (!textRun.get()) {
return;
}
gfxPoint pt(aX, aY);
Range range(0, aLength);
if (mTextRunRTL) {
if (mVertical) {
pt.y += textRun->GetAdvanceWidth(range, &provider);
} else {
pt.x += textRun->GetAdvanceWidth(range, &provider);
}
}
gfxTextRun::DrawParams params(aContext->ThebesContext());
params.provider = &provider;
textRun->Draw(range, pt, params);
}
static nsBoundingMetrics
GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t* aString,
uint32_t aLength, mozilla::gfx::DrawTarget* aDrawTarget,
gfxFont::BoundingBoxType aType)
{
if (aLength == 0)
return nsBoundingMetrics();
StubPropertyProvider provider;
AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
nsBoundingMetrics m;
if (textRun.get()) {
gfxTextRun::Metrics theMetrics = textRun->MeasureText(
gfxTextRun::Range(0, aLength), aType, aDrawTarget, &provider);
m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X());
m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost());
m.ascent = NSToCoordCeil( -theMetrics.mBoundingBox.Y());
m.descent = NSToCoordCeil( theMetrics.mBoundingBox.YMost());
m.width = NSToCoordRound( theMetrics.mAdvanceWidth);
}
return m;
}
nsBoundingMetrics
nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength,
DrawTarget* aDrawTarget)
{
return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
}
nsBoundingMetrics
nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength,
DrawTarget* aDrawTarget)
{
return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
gfxFont::LOOSE_INK_EXTENTS);
}

268
gfx/src/nsFontMetrics.h Normal file
View file

@ -0,0 +1,268 @@
/* -*- 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/. */
#ifndef NSFONTMETRICS__H__
#define NSFONTMETRICS__H__
#include <stdint.h> // for uint32_t
#include <sys/types.h> // for int32_t
#include "gfxTextRun.h" // for gfxFont, gfxFontGroup
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/RefPtr.h" // for RefPtr
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsCoord.h" // for nscoord
#include "nsError.h" // for nsresult
#include "nsFont.h" // for nsFont
#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
#include "nscore.h" // for char16_t
class gfxUserFontSet;
class gfxTextPerfMetrics;
class nsDeviceContext;
class nsIAtom;
class nsRenderingContext;
struct nsBoundingMetrics;
/**
* Font metrics
*
* This class may be somewhat misnamed. A better name might be
* nsFontList. The style system uses the nsFont struct for various
* font properties, one of which is font-family, which can contain a
* *list* of font names. The nsFont struct is "realized" by asking the
* device context to cough up an nsFontMetrics object, which contains
* a list of real font handles, one for each font mentioned in
* font-family (and for each fallback when we fall off the end of that
* list).
*
* The style system needs to have access to certain metrics, such as
* the em height (for the CSS "em" unit), and we use the first Western
* font's metrics for that purpose. The platform-specific
* implementations are expected to select non-Western fonts that "fit"
* reasonably well with the Western font that is loaded at Init time.
*/
class nsFontMetrics final
{
public:
typedef gfxTextRun::Range Range;
typedef mozilla::gfx::DrawTarget DrawTarget;
struct Params
{
nsIAtom* language = nullptr;
bool explicitLanguage = false;
gfxFont::Orientation orientation = gfxFont::eHorizontal;
gfxUserFontSet* userFontSet = nullptr;
gfxTextPerfMetrics* textPerf = nullptr;
};
nsFontMetrics(const nsFont& aFont, const Params& aParams,
nsDeviceContext *aContext);
NS_INLINE_DECL_REFCOUNTING(nsFontMetrics)
/**
* Destroy this font metrics. This breaks the association between
* the font metrics and the device context.
*/
void Destroy();
/**
* Return the font's x-height.
*/
nscoord XHeight();
/**
* Return the font's cap-height.
*/
nscoord CapHeight();
/**
* Return the font's superscript offset (the distance from the
* baseline to where a superscript's baseline should be placed).
* The value returned will be positive.
*/
nscoord SuperscriptOffset();
/**
* Return the font's subscript offset (the distance from the
* baseline to where a subscript's baseline should be placed).
* The value returned will be positive.
*/
nscoord SubscriptOffset();
/**
* Return the font's strikeout offset (the distance from the
* baseline to where a strikeout should be placed) and size.
* Positive values are above the baseline, negative below.
*/
void GetStrikeout(nscoord& aOffset, nscoord& aSize);
/**
* Return the font's underline offset (the distance from the
* baseline to where a underline should be placed) and size.
* Positive values are above the baseline, negative below.
*/
void GetUnderline(nscoord& aOffset, nscoord& aSize);
/**
* Returns the amount of internal leading for the font.
* This is normally the difference between the max ascent
* and the em ascent.
*/
nscoord InternalLeading();
/**
* Returns the amount of external leading for the font.
* em ascent(?) plus external leading is the font designer's
* recommended line-height for this font.
*/
nscoord ExternalLeading();
/**
* Returns the height of the em square.
* This is em ascent plus em descent.
*/
nscoord EmHeight();
/**
* Returns the ascent part of the em square.
*/
nscoord EmAscent();
/**
* Returns the descent part of the em square.
*/
nscoord EmDescent();
/**
* Returns the height of the bounding box.
* This is max ascent plus max descent.
*/
nscoord MaxHeight();
/**
* Returns the maximum distance characters in this font extend
* above the base line.
*/
nscoord MaxAscent();
/**
* Returns the maximum distance characters in this font extend
* below the base line.
*/
nscoord MaxDescent();
/**
* Returns the maximum character advance for the font.
*/
nscoord MaxAdvance();
/**
* Returns the average character width
*/
nscoord AveCharWidth();
/**
* Returns the often needed width of the space character
*/
nscoord SpaceWidth();
/**
* Returns the font associated with these metrics. The return value
* is only defined after Init() has been called.
*/
const nsFont &Font() const { return mFont; }
/**
* Returns the language associated with these metrics
*/
nsIAtom* Language() const { return mLanguage; }
/**
* Returns the orientation (horizontal/vertical) of these metrics.
*/
gfxFont::Orientation Orientation() const { return mOrientation; }
int32_t GetMaxStringLength();
// Get the width for this string. aWidth will be updated with the
// width in points, not twips. Callers must convert it if they
// want it in another format.
nscoord GetWidth(const char* aString, uint32_t aLength,
DrawTarget* aDrawTarget);
nscoord GetWidth(const char16_t* aString, uint32_t aLength,
DrawTarget* aDrawTarget);
// Draw a string using this font handle on the surface passed in.
void DrawString(const char *aString, uint32_t aLength,
nscoord aX, nscoord aY,
nsRenderingContext *aContext);
void DrawString(const char16_t* aString, uint32_t aLength,
nscoord aX, nscoord aY,
nsRenderingContext *aContext,
DrawTarget* aTextRunConstructionDrawTarget);
nsBoundingMetrics GetBoundingMetrics(const char16_t *aString,
uint32_t aLength,
DrawTarget* aDrawTarget);
// Returns the LOOSE_INK_EXTENTS bounds of the text for determing the
// overflow area of the string.
nsBoundingMetrics GetInkBoundsForVisualOverflow(const char16_t *aString,
uint32_t aLength,
DrawTarget* aDrawTarget);
void SetTextRunRTL(bool aIsRTL) { mTextRunRTL = aIsRTL; }
bool GetTextRunRTL() const { return mTextRunRTL; }
void SetVertical(bool aVertical) { mVertical = aVertical; }
bool GetVertical() const { return mVertical; }
void SetTextOrientation(uint8_t aTextOrientation)
{
mTextOrientation = aTextOrientation;
}
uint8_t GetTextOrientation() const { return mTextOrientation; }
gfxFontGroup* GetThebesFontGroup() const { return mFontGroup; }
gfxUserFontSet* GetUserFontSet() const
{
return mFontGroup->GetUserFontSet();
}
int32_t AppUnitsPerDevPixel() const { return mP2A; }
private:
// Private destructor, to discourage deletion outside of Release():
~nsFontMetrics();
const gfxFont::Metrics& GetMetrics() const {
return GetMetrics(mOrientation);
}
const gfxFont::Metrics&
GetMetrics(const gfxFont::Orientation aFontOrientation) const;
nsFont mFont;
RefPtr<gfxFontGroup> mFontGroup;
nsCOMPtr<nsIAtom> mLanguage;
nsDeviceContext* mDeviceContext;
int32_t mP2A;
// The font orientation (horizontal or vertical) for which these metrics
// have been initialized. This determines which line metrics (ascent and
// descent) they will return.
gfxFont::Orientation mOrientation;
// These fields may be set by clients to control the behavior of methods
// like GetWidth and DrawString according to the writing mode, direction
// and text-orientation desired.
bool mTextRunRTL;
bool mVertical;
uint8_t mTextOrientation;
};
#endif /* NSFONTMETRICS__H__ */

21
gfx/src/nsGfxCIID.h Normal file
View file

@ -0,0 +1,21 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef nsGfxCIID_h__
#define nsGfxCIID_h__
#define NS_FONT_ENUMERATOR_CID \
{ 0xa6cf9115, 0x15b3, 0x11d2, \
{ 0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } }
#define NS_SCRIPTABLE_REGION_CID \
{ 0xda5b130a, 0x1dd1, 0x11b2, \
{ 0xad, 0x47, 0xf4, 0x55, 0xb1, 0x81, 0x4a, 0x78 } }
#define NS_GFX_INITIALIZATION_CID \
{ 0x67c41576, 0x9664, 0x4ed5, \
{ 0x90, 0xc1, 0xf6, 0x68, 0x3f, 0xd5, 0x2c, 0x8f } }
#endif

View file

@ -0,0 +1,61 @@
/* -*- Mode: IDL; tab-width: 2; 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 "nsISupports.idl"
[scriptable, uuid(924d98d9-3518-4cb4-8708-c74fe8e3ec3c)]
interface nsIFontEnumerator : nsISupports
{
/**
* Return a sorted array of the names of all installed fonts.
*
* @param aCount returns number of names returned
* @param aResult returns array of names
* @return void
*/
void EnumerateAllFonts(out uint32_t aCount,
[retval, array, size_is(aCount)] out wstring aResult);
/**
* Return a sorted array of names of fonts that support the given language
* group and are suitable for use as the given CSS generic font.
*
* @param aLangGroup language group
* @param aGeneric CSS generic font
* @param aCount returns number of names returned
* @param aResult returns array of names
* @return void
*/
void EnumerateFonts(in string aLangGroup, in string aGeneric,
out uint32_t aCount, [retval, array, size_is(aCount)] out wstring aResult);
/**
@param aLangGroup language group
@return bool do we have a font for this language group
*/
void HaveFontFor(in string aLangGroup, [retval] out boolean aResult);
/**
* @param aLangGroup language group
* @param aGeneric CSS generic font
* @return suggested default font for this language group and generic family
*/
wstring getDefaultFont(in string aLangGroup, in string aGeneric);
/**
* update the global font list
* return true if font list is changed
*/
boolean updateFontList();
/**
* get the standard family name on the system from given family
* @param aName family name which may be alias
* @return the standard family name on the system, if given name does not
* exist, returns empty string
*/
wstring getStandardFamilyName(in wstring aName);
};

View file

@ -0,0 +1,176 @@
/* -*- Mode: IDL; tab-width: 2; 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 "nsISupports.idl"
%{C++
#include "nsRegionFwd.h"
%}
native nsIntRegion(nsIntRegion);
[scriptable, uuid(a5f44cc7-2820-489b-b817-ae8a08506ff6)]
interface nsIScriptableRegion : nsISupports
{
void init ( ) ;
/**
* copy operator equivalent that takes another region
*
* @param region to copy
* @return void
*
**/
void setToRegion ( in nsIScriptableRegion aRegion );
/**
* copy operator equivalent that takes a rect
*
* @param aX xoffset of rect to set region to
* @param aY yoffset of rect to set region to
* @param aWidth width of rect to set region to
* @param aHeight height of rect to set region to
* @return void
*
**/
void setToRect ( in long aX, in long aY, in long aWidth, in long aHeight );
/**
* destructively intersect another region with this one
*
* @param region to intersect
* @return void
*
**/
void intersectRegion ( in nsIScriptableRegion aRegion ) ;
/**
* destructively intersect a rect with this region
*
* @param aX xoffset of rect to intersect with region
* @param aY yoffset of rect to intersect with region
* @param aWidth width of rect to intersect with region
* @param aHeight height of rect to intersect with region
* @return void
*
**/
void intersectRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
/**
* destructively union another region with this one
*
* @param region to union
* @return void
*
**/
void unionRegion ( in nsIScriptableRegion aRegion ) ;
/**
* destructively union a rect with this region
*
* @param aX xoffset of rect to union with region
* @param aY yoffset of rect to union with region
* @param aWidth width of rect to union with region
* @param aHeight height of rect to union with region
* @return void
*
**/
void unionRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
/**
* destructively subtract another region with this one
*
* @param region to subtract
* @return void
*
**/
void subtractRegion ( in nsIScriptableRegion aRegion ) ;
/**
* destructively subtract a rect from this region
*
* @param aX xoffset of rect to subtract with region
* @param aY yoffset of rect to subtract with region
* @param aWidth width of rect to subtract with region
* @param aHeight height of rect to subtract with region
* @return void
*
**/
void subtractRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
/**
* is this region empty? i.e. does it contain any pixels
*
* @param none
* @return returns whether the region is empty
*
**/
boolean isEmpty ( ) ;
/**
* == operator equivalent i.e. do the regions contain exactly
* the same pixels
*
* @param region to compare
* @return whether the regions are identical
*
**/
boolean isEqualRegion ( in nsIScriptableRegion aRegion ) ;
/**
* returns the bounding box of the region i.e. the smallest
* rectangle that completely contains the region.
*
* @param aX out parameter for xoffset of bounding rect for region
* @param aY out parameter for yoffset of bounding rect for region
* @param aWidth out parameter for width of bounding rect for region
* @param aHeight out parameter for height of bounding rect for region
* @return void
*
**/
void getBoundingBox ( out long aX, out long aY, out long aWidth, out long aHeight ) ;
/**
* offsets the region in x and y
*
* @param xoffset pixel offset in x
* @param yoffset pixel offset in y
* @return void
*
**/
void offset ( in long aXOffset, in long aYOffset ) ;
/**
* @return null if there are no rects,
* @return flat array of rects,ie [x1,y1,width1,height1,x2...].
* The result will contain bogus data if values don't fit in 31 bit
**/
[implicit_jscontext] jsval getRects();
/**
* does the region intersect the rectangle?
*
* @param rect to check for containment
* @return true if the region intersects the rect
*
**/
boolean containsRect ( in long aX, in long aY, in long aWidth, in long aHeight ) ;
[noscript] readonly attribute nsIntRegion region;
};

206
gfx/src/nsITheme.h Normal file
View file

@ -0,0 +1,206 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
/* service providing platform-specific native rendering for widgets */
#ifndef nsITheme_h_
#define nsITheme_h_
#include "nsISupports.h"
#include "nsCOMPtr.h"
#include "nsColor.h"
#include "Units.h"
struct nsRect;
class nsAttrValue;
class nsPresContext;
class nsRenderingContext;
class nsDeviceContext;
class nsIFrame;
class nsIAtom;
class nsIWidget;
// IID for the nsITheme interface
// {7329f760-08cb-450f-8225-dae729096dec}
#define NS_ITHEME_IID \
{ 0x7329f760, 0x08cb, 0x450f, \
{ 0x82, 0x25, 0xda, 0xe7, 0x29, 0x09, 0x6d, 0xec } }
// {0ae05515-cf7a-45a8-9e02-6556de7685b1}
#define NS_THEMERENDERER_CID \
{ 0x0ae05515, 0xcf7a, 0x45a8, \
{ 0x9e, 0x02, 0x65, 0x56, 0xde, 0x76, 0x85, 0xb1 } }
/**
* nsITheme is a service that provides platform-specific native
* rendering for widgets. In other words, it provides the necessary
* operations to draw a rendering object (an nsIFrame) as a native
* widget.
*
* All the methods on nsITheme take a rendering context or device
* context, a frame (the rendering object), and a widget type (one of
* the constants in nsThemeConstants.h).
*/
class nsITheme: public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITHEME_IID)
/**
* Draw the actual theme background.
* @param aContext the context to draw into
* @param aFrame the frame for the widget that we're drawing
* @param aWidgetType the -moz-appearance value to draw
* @param aRect the rectangle defining the area occupied by the widget
* @param aDirtyRect the rectangle that needs to be drawn
*/
NS_IMETHOD DrawWidgetBackground(nsRenderingContext* aContext,
nsIFrame* aFrame,
uint8_t aWidgetType,
const nsRect& aRect,
const nsRect& aDirtyRect) = 0;
/**
* Get the computed CSS border for the widget, in pixels.
*/
NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext,
nsIFrame* aFrame,
uint8_t aWidgetType,
nsIntMargin* aResult)=0;
/**
* This method can return false to indicate that the CSS padding
* value should be used. Otherwise, it will fill in aResult with the
* computed padding, in pixels, and return true.
*
* XXXldb This ought to be required to return true for non-containers
* so that we don't let specified padding that has no effect change
* the computed padding and potentially the size.
*/
virtual bool GetWidgetPadding(nsDeviceContext* aContext,
nsIFrame* aFrame,
uint8_t aWidgetType,
nsIntMargin* aResult) = 0;
/**
* On entry, *aResult is positioned at 0,0 and sized to the new size
* of aFrame (aFrame->GetSize() may be stale and should not be used).
* This method can return false to indicate that no special
* overflow area is required by the native widget. Otherwise it will
* fill in aResult with the desired overflow area, in appunits, relative
* to the frame origin, and return true.
*
* This overflow area is used to determine what area needs to be
* repainted when the widget changes. However, it does not affect the
* widget's size or what area is reachable by scrollbars. (In other
* words, in layout terms, it affects visual overflow but not
* scrollable overflow.)
*/
virtual bool GetWidgetOverflow(nsDeviceContext* aContext,
nsIFrame* aFrame,
uint8_t aWidgetType,
/*INOUT*/ nsRect* aOverflowRect)
{ return false; }
/**
* Get the minimum border-box size of a widget, in *pixels* (in
* |aResult|). If |aIsOverridable| is set to true, this size is a
* minimum size; if false, this size is the only valid size for the
* widget.
*/
NS_IMETHOD GetMinimumWidgetSize(nsPresContext* aPresContext,
nsIFrame* aFrame,
uint8_t aWidgetType,
mozilla::LayoutDeviceIntSize* aResult,
bool* aIsOverridable)=0;
enum Transparency {
eOpaque = 0,
eTransparent,
eUnknownTransparency
};
/**
* Returns what we know about the transparency of the widget.
*/
virtual Transparency GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
{ return eUnknownTransparency; }
NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
nsIAtom* aAttribute, bool* aShouldRepaint,
const nsAttrValue* aOldValue)=0;
NS_IMETHOD ThemeChanged()=0;
virtual bool WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
{ return false; }
virtual bool NeedToClearBackgroundBehindWidget(nsIFrame* aFrame,
uint8_t aWidgetType)
{ return false; }
virtual bool WidgetProvidesFontSmoothingBackgroundColor(nsIFrame* aFrame,
uint8_t aWidgetType, nscolor* aColor)
{ return false; }
/**
* ThemeGeometryType values are used for describing themed nsIFrames in
* calls to nsIWidget::UpdateThemeGeometries. We don't simply pass the
* -moz-appearance value ("widget type") of the frame because the widget may
* want to treat different frames with the same -moz-appearance differently
* based on other properties of the frame. So we give the theme a first look
* at the frame in nsITheme::ThemeGeometryTypeForWidget and pass the
* returned ThemeGeometryType along to the widget.
* Each theme backend defines the ThemeGeometryType values it needs in its
* own nsITheme subclass. eThemeGeometryTypeUnknown is the only value that's
* shared between backends.
*/
typedef uint8_t ThemeGeometryType;
enum {
eThemeGeometryTypeUnknown = 0
};
/**
* Returns the theme geometry type that should be used in the ThemeGeometry
* array that's passed to the widget using nsIWidget::UpdateThemeGeometries.
* A return value of eThemeGeometryTypeUnknown means that this frame will
* not be included in the ThemeGeometry array.
*/
virtual ThemeGeometryType ThemeGeometryTypeForWidget(nsIFrame* aFrame,
uint8_t aWidgetType)
{ return eThemeGeometryTypeUnknown; }
/**
* Can the nsITheme implementation handle this widget?
*/
virtual bool ThemeSupportsWidget(nsPresContext* aPresContext,
nsIFrame* aFrame,
uint8_t aWidgetType)=0;
virtual bool WidgetIsContainer(uint8_t aWidgetType)=0;
/**
* Does the nsITheme implementation draw its own focus ring for this widget?
*/
virtual bool ThemeDrawsFocusForWidget(uint8_t aWidgetType)=0;
/**
* Should we insert a dropmarker inside of combobox button?
*/
virtual bool ThemeNeedsComboboxDropmarker()=0;
/**
* Should we hide scrollbars?
*/
virtual bool ShouldHideScrollbars()
{ return false; }
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsITheme, NS_ITHEME_IID)
// Creator function
extern nsresult NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult);
#endif

26
gfx/src/nsMargin.h Normal file
View file

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef NSMARGIN_H
#define NSMARGIN_H
#include "nsCoord.h"
#include "nsPoint.h"
#include "mozilla/gfx/BaseMargin.h"
#include "mozilla/gfx/Rect.h"
struct nsMargin : public mozilla::gfx::BaseMargin<nscoord, nsMargin> {
typedef mozilla::gfx::BaseMargin<nscoord, nsMargin> Super;
// Constructors
nsMargin() : Super() {}
nsMargin(const nsMargin& aMargin) : Super(aMargin) {}
nsMargin(nscoord aTop, nscoord aRight, nscoord aBottom, nscoord aLeft)
: Super(aTop, aRight, aBottom, aLeft) {}
};
typedef mozilla::gfx::IntMargin nsIntMargin;
#endif /* NSMARGIN_H */

106
gfx/src/nsPoint.h Normal file
View file

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef NSPOINT_H
#define NSPOINT_H
#include "nsCoord.h"
#include "mozilla/gfx/BaseSize.h"
#include "mozilla/gfx/BasePoint.h"
#include "nsSize.h"
#include "mozilla/gfx/Point.h"
// nsIntPoint represents a point in one of the types of pixels.
// Uses of nsIntPoint should eventually be converted to CSSIntPoint,
// LayoutDeviceIntPoint, etc. (see layout/base/Units.h).
typedef mozilla::gfx::IntPoint nsIntPoint;
// nsPoint represents a point in app units.
struct nsPoint : public mozilla::gfx::BasePoint<nscoord, nsPoint> {
typedef mozilla::gfx::BasePoint<nscoord, nsPoint> Super;
nsPoint() : Super() {}
nsPoint(const nsPoint& aPoint) : Super(aPoint) {}
nsPoint(nscoord aX, nscoord aY) : Super(aX, aY) {}
inline nsIntPoint ScaleToNearestPixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const;
inline nsIntPoint ToNearestPixels(nscoord aAppUnitsPerPixel) const;
/**
* Return this point scaled to a different appunits per pixel (APP) ratio.
* @param aFromAPP the APP to scale from
* @param aToAPP the APP to scale to
*/
MOZ_MUST_USE inline nsPoint
ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const;
MOZ_MUST_USE inline nsPoint
RemoveResolution(const float resolution) const;
MOZ_MUST_USE inline nsPoint
ApplyResolution(const float resolution) const;
};
inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel);
inline nsIntPoint
nsPoint::ScaleToNearestPixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const
{
return nsIntPoint(
NSToIntRoundUp(NSAppUnitsToDoublePixels(x, aAppUnitsPerPixel) * aXScale),
NSToIntRoundUp(NSAppUnitsToDoublePixels(y, aAppUnitsPerPixel) * aYScale));
}
inline nsIntPoint
nsPoint::ToNearestPixels(nscoord aAppUnitsPerPixel) const
{
return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel);
}
inline nsPoint
nsPoint::ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const
{
if (aFromAPP != aToAPP) {
nsPoint point;
point.x = NSToCoordRound(NSCoordScale(x, aFromAPP, aToAPP));
point.y = NSToCoordRound(NSCoordScale(y, aFromAPP, aToAPP));
return point;
}
return *this;
}
inline nsPoint
nsPoint::RemoveResolution(const float resolution) const {
if (resolution != 1.0f) {
nsPoint point;
point.x = NSToCoordRound(NSCoordToFloat(x) / resolution);
point.y = NSToCoordRound(NSCoordToFloat(y) / resolution);
return point;
}
return *this;
}
inline nsPoint
nsPoint::ApplyResolution(const float resolution) const {
if (resolution != 1.0f) {
nsPoint point;
point.x = NSToCoordRound(NSCoordToFloat(x) * resolution);
point.y = NSToCoordRound(NSCoordToFloat(y) * resolution);
return point;
}
return *this;
}
// app units are integer multiples of pixels, so no rounding needed
inline nsPoint
ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel)
{
return nsPoint(NSIntPixelsToAppUnits(aPoint.x, aAppUnitsPerPixel),
NSIntPixelsToAppUnits(aPoint.y, aAppUnitsPerPixel));
}
#endif /* NSPOINT_H */

62
gfx/src/nsRect.cpp Normal file
View file

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsRect.h"
#include "mozilla/gfx/Types.h" // for NS_SIDE_BOTTOM, etc
#include "mozilla/CheckedInt.h" // for CheckedInt
#include "nsDeviceContext.h" // for nsDeviceContext
#include "nsString.h" // for nsAutoString, etc
#include "nsMargin.h" // for nsMargin
static_assert((int(NS_SIDE_TOP) == 0) &&
(int(NS_SIDE_RIGHT) == 1) &&
(int(NS_SIDE_BOTTOM) == 2) &&
(int(NS_SIDE_LEFT) == 3),
"The mozilla::css::Side sequence must match the nsMargin nscoord sequence");
const mozilla::gfx::IntRect& GetMaxSizedIntRect() {
static const mozilla::gfx::IntRect r(0, 0, INT32_MAX, INT32_MAX);
return r;
}
bool nsRect::Overflows() const {
#ifdef NS_COORD_IS_FLOAT
return false;
#else
mozilla::CheckedInt<int32_t> xMost = this->x;
xMost += this->width;
mozilla::CheckedInt<int32_t> yMost = this->y;
yMost += this->height;
return !xMost.isValid() || !yMost.isValid();
#endif
}
#ifdef DEBUG
// Diagnostics
FILE* operator<<(FILE* out, const nsRect& rect)
{
nsAutoString tmp;
// Output the coordinates in fractional pixels so they're easier to read
tmp.Append('{');
tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.x,
nsDeviceContext::AppUnitsPerCSSPixel()));
tmp.AppendLiteral(", ");
tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.y,
nsDeviceContext::AppUnitsPerCSSPixel()));
tmp.AppendLiteral(", ");
tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.width,
nsDeviceContext::AppUnitsPerCSSPixel()));
tmp.AppendLiteral(", ");
tmp.AppendFloat(NSAppUnitsToFloatPixels(rect.height,
nsDeviceContext::AppUnitsPerCSSPixel()));
tmp.Append('}');
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
return out;
}
#endif // DEBUG

324
gfx/src/nsRect.h Normal file
View file

@ -0,0 +1,324 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef NSRECT_H
#define NSRECT_H
#include <stdio.h> // for FILE
#include <stdint.h> // for int32_t, int64_t
#include <algorithm> // for min/max
#include "mozilla/Likely.h" // for MOZ_UNLIKELY
#include "mozilla/gfx/Rect.h"
#include "nsCoord.h" // for nscoord, etc
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsPoint.h" // for nsIntPoint, nsPoint
#include "nsMargin.h" // for nsIntMargin, nsMargin
#include "nsSize.h" // for IntSize, nsSize
#include "nscore.h" // for NS_BUILD_REFCNT_LOGGING
typedef mozilla::gfx::IntRect nsIntRect;
struct nsRect :
public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> {
typedef mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> Super;
static void VERIFY_COORD(nscoord aValue) { ::VERIFY_COORD(aValue); }
// Constructors
nsRect() : Super()
{
MOZ_COUNT_CTOR(nsRect);
}
nsRect(const nsRect& aRect) : Super(aRect)
{
MOZ_COUNT_CTOR(nsRect);
}
nsRect(const nsPoint& aOrigin, const nsSize &aSize) : Super(aOrigin, aSize)
{
MOZ_COUNT_CTOR(nsRect);
}
nsRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) :
Super(aX, aY, aWidth, aHeight)
{
MOZ_COUNT_CTOR(nsRect);
}
#ifdef NS_BUILD_REFCNT_LOGGING
~nsRect() {
MOZ_COUNT_DTOR(nsRect);
}
#endif
// We have saturating versions of all the Union methods. These avoid
// overflowing nscoord values in the 'width' and 'height' fields by
// clamping the width and height values to nscoord_MAX if necessary.
MOZ_MUST_USE nsRect SaturatingUnion(const nsRect& aRect) const
{
if (IsEmpty()) {
return aRect;
} else if (aRect.IsEmpty()) {
return *static_cast<const nsRect*>(this);
} else {
return SaturatingUnionEdges(aRect);
}
}
MOZ_MUST_USE nsRect SaturatingUnionEdges(const nsRect& aRect) const
{
#ifdef NS_COORD_IS_FLOAT
return UnionEdges(aRect);
#else
nsRect result;
result.x = std::min(aRect.x, x);
int64_t w = std::max(int64_t(aRect.x) + aRect.width, int64_t(x) + width) - result.x;
if (MOZ_UNLIKELY(w > nscoord_MAX)) {
// Clamp huge negative x to nscoord_MIN / 2 and try again.
result.x = std::max(result.x, nscoord_MIN / 2);
w = std::max(int64_t(aRect.x) + aRect.width, int64_t(x) + width) - result.x;
if (MOZ_UNLIKELY(w > nscoord_MAX)) {
w = nscoord_MAX;
}
}
result.width = nscoord(w);
result.y = std::min(aRect.y, y);
int64_t h = std::max(int64_t(aRect.y) + aRect.height, int64_t(y) + height) - result.y;
if (MOZ_UNLIKELY(h > nscoord_MAX)) {
// Clamp huge negative y to nscoord_MIN / 2 and try again.
result.y = std::max(result.y, nscoord_MIN / 2);
h = std::max(int64_t(aRect.y) + aRect.height, int64_t(y) + height) - result.y;
if (MOZ_UNLIKELY(h > nscoord_MAX)) {
h = nscoord_MAX;
}
}
result.height = nscoord(h);
return result;
#endif
}
#ifndef NS_COORD_IS_FLOAT
// Make all nsRect Union methods be saturating.
MOZ_MUST_USE nsRect UnionEdges(const nsRect& aRect) const
{
return SaturatingUnionEdges(aRect);
}
void UnionRectEdges(const nsRect& aRect1, const nsRect& aRect2)
{
*this = aRect1.UnionEdges(aRect2);
}
MOZ_MUST_USE nsRect Union(const nsRect& aRect) const
{
return SaturatingUnion(aRect);
}
void UnionRect(const nsRect& aRect1, const nsRect& aRect2)
{
*this = aRect1.Union(aRect2);
}
#endif
void SaturatingUnionRect(const nsRect& aRect1, const nsRect& aRect2)
{
*this = aRect1.SaturatingUnion(aRect2);
}
void SaturatingUnionRectEdges(const nsRect& aRect1, const nsRect& aRect2)
{
*this = aRect1.SaturatingUnionEdges(aRect2);
}
// Return whether this rect's right or bottom edge overflow int32.
bool Overflows() const;
/**
* Return this rect scaled to a different appunits per pixel (APP) ratio.
* In the RoundOut version we make the rect the smallest rect containing the
* unrounded result. In the RoundIn version we make the rect the largest rect
* contained in the unrounded result.
* @param aFromAPP the APP to scale from
* @param aToAPP the APP to scale to
* @note this can turn an empty rectangle into a non-empty rectangle
*/
MOZ_MUST_USE inline nsRect
ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const;
MOZ_MUST_USE inline nsRect
ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, int32_t aToAPP) const;
MOZ_MUST_USE inline mozilla::gfx::IntRect
ScaleToNearestPixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const;
MOZ_MUST_USE inline mozilla::gfx::IntRect
ToNearestPixels(nscoord aAppUnitsPerPixel) const;
// Note: this can turn an empty rectangle into a non-empty rectangle
MOZ_MUST_USE inline mozilla::gfx::IntRect
ScaleToOutsidePixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const;
// Note: this can turn an empty rectangle into a non-empty rectangle
MOZ_MUST_USE inline mozilla::gfx::IntRect
ToOutsidePixels(nscoord aAppUnitsPerPixel) const;
MOZ_MUST_USE inline mozilla::gfx::IntRect
ScaleToInsidePixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const;
MOZ_MUST_USE inline mozilla::gfx::IntRect
ToInsidePixels(nscoord aAppUnitsPerPixel) const;
// This is here only to keep IPDL-generated code happy. DO NOT USE.
bool operator==(const nsRect& aRect) const
{
return IsEqualEdges(aRect);
}
MOZ_MUST_USE inline nsRect RemoveResolution(const float aResolution) const;
};
/*
* App Unit/Pixel conversions
*/
inline nsRect
nsRect::ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const
{
if (aFromAPP == aToAPP) {
return *this;
}
nsRect rect;
nscoord right = NSToCoordCeil(NSCoordScale(XMost(), aFromAPP, aToAPP));
nscoord bottom = NSToCoordCeil(NSCoordScale(YMost(), aFromAPP, aToAPP));
rect.x = NSToCoordFloor(NSCoordScale(x, aFromAPP, aToAPP));
rect.y = NSToCoordFloor(NSCoordScale(y, aFromAPP, aToAPP));
rect.width = (right - rect.x);
rect.height = (bottom - rect.y);
return rect;
}
inline nsRect
nsRect::ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP, int32_t aToAPP) const
{
if (aFromAPP == aToAPP) {
return *this;
}
nsRect rect;
nscoord right = NSToCoordFloor(NSCoordScale(XMost(), aFromAPP, aToAPP));
nscoord bottom = NSToCoordFloor(NSCoordScale(YMost(), aFromAPP, aToAPP));
rect.x = NSToCoordCeil(NSCoordScale(x, aFromAPP, aToAPP));
rect.y = NSToCoordCeil(NSCoordScale(y, aFromAPP, aToAPP));
rect.width = (right - rect.x);
rect.height = (bottom - rect.y);
return rect;
}
// scale the rect but round to preserve centers
inline mozilla::gfx::IntRect
nsRect::ScaleToNearestPixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const
{
mozilla::gfx::IntRect rect;
rect.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(x, aAppUnitsPerPixel) * aXScale);
rect.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(y, aAppUnitsPerPixel) * aYScale);
// Avoid negative widths and heights due to overflow
rect.width = std::max(0, NSToIntRoundUp(NSAppUnitsToDoublePixels(XMost(),
aAppUnitsPerPixel) * aXScale) - rect.x);
rect.height = std::max(0, NSToIntRoundUp(NSAppUnitsToDoublePixels(YMost(),
aAppUnitsPerPixel) * aYScale) - rect.y);
return rect;
}
// scale the rect but round to smallest containing rect
inline mozilla::gfx::IntRect
nsRect::ScaleToOutsidePixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const
{
mozilla::gfx::IntRect rect;
rect.x = NSToIntFloor(NSAppUnitsToFloatPixels(x, float(aAppUnitsPerPixel)) * aXScale);
rect.y = NSToIntFloor(NSAppUnitsToFloatPixels(y, float(aAppUnitsPerPixel)) * aYScale);
// Avoid negative widths and heights due to overflow
rect.width = std::max(0, NSToIntCeil(NSAppUnitsToFloatPixels(XMost(),
float(aAppUnitsPerPixel)) * aXScale) - rect.x);
rect.height = std::max(0, NSToIntCeil(NSAppUnitsToFloatPixels(YMost(),
float(aAppUnitsPerPixel)) * aYScale) - rect.y);
return rect;
}
// scale the rect but round to largest contained rect
inline mozilla::gfx::IntRect
nsRect::ScaleToInsidePixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const
{
mozilla::gfx::IntRect rect;
rect.x = NSToIntCeil(NSAppUnitsToFloatPixels(x, float(aAppUnitsPerPixel)) * aXScale);
rect.y = NSToIntCeil(NSAppUnitsToFloatPixels(y, float(aAppUnitsPerPixel)) * aYScale);
// Avoid negative widths and heights due to overflow
rect.width = std::max(0, NSToIntFloor(NSAppUnitsToFloatPixels(XMost(),
float(aAppUnitsPerPixel)) * aXScale) - rect.x);
rect.height = std::max(0, NSToIntFloor(NSAppUnitsToFloatPixels(YMost(),
float(aAppUnitsPerPixel)) * aYScale) - rect.y);
return rect;
}
inline mozilla::gfx::IntRect
nsRect::ToNearestPixels(nscoord aAppUnitsPerPixel) const
{
return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel);
}
inline mozilla::gfx::IntRect
nsRect::ToOutsidePixels(nscoord aAppUnitsPerPixel) const
{
return ScaleToOutsidePixels(1.0f, 1.0f, aAppUnitsPerPixel);
}
inline mozilla::gfx::IntRect
nsRect::ToInsidePixels(nscoord aAppUnitsPerPixel) const
{
return ScaleToInsidePixels(1.0f, 1.0f, aAppUnitsPerPixel);
}
inline nsRect
nsRect::RemoveResolution(const float aResolution) const
{
MOZ_ASSERT(aResolution > 0.0f);
nsRect rect;
rect.x = NSToCoordRound(NSCoordToFloat(x) / aResolution);
rect.y = NSToCoordRound(NSCoordToFloat(y) / aResolution);
// A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
// rect as well instead of possibly rounding the width or height to zero.
if (width == 1 && height == 1) {
rect.width = rect.height = 1;
} else {
rect.width = NSToCoordCeil(NSCoordToFloat(width) / aResolution);
rect.height = NSToCoordCeil(NSCoordToFloat(height) / aResolution);
}
return rect;
}
const mozilla::gfx::IntRect& GetMaxSizedIntRect();
// app units are integer multiples of pixels, so no rounding needed
template<class units>
nsRect
ToAppUnits(const mozilla::gfx::IntRectTyped<units>& aRect, nscoord aAppUnitsPerPixel)
{
return nsRect(NSIntPixelsToAppUnits(aRect.x, aAppUnitsPerPixel),
NSIntPixelsToAppUnits(aRect.y, aAppUnitsPerPixel),
NSIntPixelsToAppUnits(aRect.width, aAppUnitsPerPixel),
NSIntPixelsToAppUnits(aRect.height, aAppUnitsPerPixel));
}
#ifdef DEBUG
// Diagnostics
extern FILE* operator<<(FILE* out, const nsRect& rect);
#endif // DEBUG
#endif /* NSRECT_H */

1146
gfx/src/nsRegion.cpp Normal file

File diff suppressed because it is too large Load diff

868
gfx/src/nsRegion.h Normal file
View file

@ -0,0 +1,868 @@
/* -*- 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/. */
#ifndef nsRegion_h__
#define nsRegion_h__
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint64_t
#include <sys/types.h> // for int32_t
#include <ostream> // for std::ostream
#include "nsCoord.h" // for nscoord
#include "nsError.h" // for nsresult
#include "nsPoint.h" // for nsIntPoint, nsPoint
#include "nsRect.h" // for mozilla::gfx::IntRect, nsRect
#include "nsMargin.h" // for nsIntMargin
#include "nsRegionFwd.h" // for nsIntRegion
#include "nsStringGlue.h" // for nsCString
#include "xpcom-config.h" // for CPP_THROW_NEW
#include "mozilla/ArrayView.h" // for ArrayView
#include "mozilla/Move.h" // for mozilla::Move
#include "mozilla/gfx/MatrixFwd.h" // for mozilla::gfx::Matrix4x4
#include "pixman.h"
/* For information on the internal representation look at pixman-region.c
*
* This replaces an older homebrew implementation of nsRegion. The
* representation used here may use more rectangles than nsRegion however, the
* representation is canonical. This means that there's no need for an
* Optimize() method because for a paticular region there is only one
* representation. This means that nsIntRegion will have more predictable
* performance characteristics than the old nsRegion and should not become
* degenerate.
*
* The pixman region code originates from X11 which has spread to a variety of
* projects including Qt, Gtk, Wine. It should perform reasonably well.
*/
enum class VisitSide {
TOP,
BOTTOM,
LEFT,
RIGHT
};
class nsRegion
{
public:
typedef nsRect RectType;
typedef nsPoint PointType;
typedef nsMargin MarginType;
nsRegion () { pixman_region32_init(&mImpl); }
MOZ_IMPLICIT nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl,
aRect.x,
aRect.y,
aRect.width,
aRect.height); }
explicit nsRegion (mozilla::gfx::ArrayView<pixman_box32_t> aRects)
{
pixman_region32_init_rects(&mImpl, aRects.Data(), aRects.Length());
}
nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); }
nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); }
nsRegion& operator = (nsRegion&& aRegion) {
pixman_region32_fini(&mImpl);
mImpl = aRegion.mImpl;
pixman_region32_init(&aRegion.mImpl);
return *this;
}
~nsRegion () { pixman_region32_fini(&mImpl); }
nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; }
nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; }
bool operator==(const nsRegion& aRgn) const
{
return IsEqual(aRgn);
}
bool operator!=(const nsRegion& aRgn) const
{
return !(*this == aRgn);
}
friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m);
void Swap(nsRegion* aOther)
{
pixman_region32_t tmp = mImpl;
mImpl = aOther->mImpl;
aOther->mImpl = tmp;
}
static
nsresult InitStatic()
{
return NS_OK;
}
static
void ShutdownStatic() {}
void AndWith(const nsRegion& aOther)
{
And(*this, aOther);
}
void AndWith(const nsRect& aOther)
{
And(*this, aOther);
}
nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2)
{
pixman_region32_intersect(&mImpl, aRgn1.Impl(), aRgn2.Impl());
return *this;
}
nsRegion& And(const nsRect& aRect, const nsRegion& aRegion)
{
return And(aRegion, aRect);
}
nsRegion& And(const nsRegion& aRegion, const nsRect& aRect)
{
pixman_region32_intersect_rect(&mImpl, aRegion.Impl(), aRect.x, aRect.y, aRect.width, aRect.height);
return *this;
}
nsRegion& And(const nsRect& aRect1, const nsRect& aRect2)
{
nsRect TmpRect;
TmpRect.IntersectRect(aRect1, aRect2);
return Copy(TmpRect);
}
nsRegion& OrWith(const nsRegion& aOther)
{
return Or(*this, aOther);
}
nsRegion& OrWith(const nsRect& aOther)
{
return Or(*this, aOther);
}
nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2)
{
pixman_region32_union(&mImpl, aRgn1.Impl(), aRgn2.Impl());
return *this;
}
nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect)
{
pixman_region32_union_rect(&mImpl, aRegion.Impl(), aRect.x, aRect.y, aRect.width, aRect.height);
return *this;
}
nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion)
{
return Or(aRegion, aRect);
}
nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2)
{
Copy (aRect1);
return Or (*this, aRect2);
}
nsRegion& XorWith(const nsRegion& aOther)
{
return Xor(*this, aOther);
}
nsRegion& XorWith(const nsRect& aOther)
{
return Xor(*this, aOther);
}
nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2)
{
// this could be implemented better if pixman had direct
// support for xoring regions.
nsRegion p;
p.Sub(aRgn1, aRgn2);
nsRegion q;
q.Sub(aRgn2, aRgn1);
return Or(p, q);
}
nsRegion& Xor(const nsRegion& aRegion, const nsRect& aRect)
{
return Xor(aRegion, nsRegion(aRect));
}
nsRegion& Xor(const nsRect& aRect, const nsRegion& aRegion)
{
return Xor(nsRegion(aRect), aRegion);
}
nsRegion& Xor(const nsRect& aRect1, const nsRect& aRect2)
{
return Xor(nsRegion(aRect1), nsRegion(aRect2));
}
nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const;
nsRegion& SubOut(const nsRegion& aOther)
{
return Sub(*this, aOther);
}
nsRegion& SubOut(const nsRect& aOther)
{
return Sub(*this, aOther);
}
nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2)
{
pixman_region32_subtract(&mImpl, aRgn1.Impl(), aRgn2.Impl());
return *this;
}
nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect)
{
return Sub(aRegion, nsRegion(aRect));
}
nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion)
{
return Sub(nsRegion(aRect), aRegion);
}
nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2)
{
Copy(aRect1);
return Sub(*this, aRect2);
}
/**
* Returns true iff the given point is inside the region. A region
* created from a rect (x=0, y=0, w=100, h=100) will NOT contain
* the point x=100, y=100.
*/
bool Contains (int aX, int aY) const
{
return pixman_region32_contains_point(Impl(), aX, aY, nullptr);
}
bool Contains (const nsRect& aRect) const
{
pixman_box32_t box = RectToBox(aRect);
return pixman_region32_contains_rectangle(Impl(), &box) == PIXMAN_REGION_IN;
}
bool Contains (const nsRegion& aRgn) const;
bool Intersects (const nsRect& aRect) const;
void MoveBy (int32_t aXOffset, int32_t aYOffset)
{
MoveBy (nsPoint (aXOffset, aYOffset));
}
void MoveBy (nsPoint aPt) { pixman_region32_translate(&mImpl, aPt.x, aPt.y); }
void SetEmpty ()
{
pixman_region32_clear(&mImpl);
}
nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const
{
return MovedBy(nsPoint(aXOffset, aYOffset));
}
nsRegion MovedBy(const nsPoint& aPt) const
{
nsRegion copy(*this);
copy.MoveBy(aPt);
return copy;
}
nsRegion Intersect(const nsRegion& aOther) const
{
nsRegion intersection;
intersection.And(*this, aOther);
return intersection;
}
void Inflate(const nsMargin& aMargin);
nsRegion Inflated(const nsMargin& aMargin) const
{
nsRegion copy(*this);
copy.Inflate(aMargin);
return copy;
}
bool IsEmpty () const { return !pixman_region32_not_empty(Impl()); }
bool IsComplex () const { return GetNumRects() > 1; }
bool IsEqual (const nsRegion& aRegion) const
{
return pixman_region32_equal(Impl(), aRegion.Impl());
}
uint32_t GetNumRects () const
{
// Work around pixman bug. Sometimes pixman creates regions with 1 rect
// that's empty.
uint32_t result = pixman_region32_n_rects(Impl());
return (result == 1 && GetBounds().IsEmpty()) ? 0 : result;
}
const nsRect GetBounds () const { return BoxToRect(mImpl.extents); }
uint64_t Area () const;
/**
* Return this region scaled to a different appunits per pixel (APP) ratio.
* This applies nsRect::ScaleToOtherAppUnitsRoundOut/In to each rect of the region.
* @param aFromAPP the APP to scale from
* @param aToAPP the APP to scale to
* @note this can turn an empty region into a non-empty region
*/
MOZ_MUST_USE nsRegion
ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const;
MOZ_MUST_USE nsRegion
ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const;
nsRegion& ScaleRoundOut(float aXScale, float aYScale);
nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale);
nsRegion& Transform (const mozilla::gfx::Matrix4x4 &aTransform);
nsIntRegion ScaleToOutsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
nsIntRegion ScaleToInsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
nsIntRegion ScaleToNearestPixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const;
nsIntRegion ToNearestPixels (nscoord aAppUnitsPerPixel) const;
/**
* Gets the largest rectangle contained in the region.
* @param aContainingRect if non-empty, we choose a rectangle that
* maximizes the area intersecting with aContainingRect (and break ties by
* then choosing the largest rectangle overall)
*/
nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const;
/**
* Make sure the region has at most aMaxRects by adding area to it
* if necessary. The simplified region will be a superset of the
* original region. The simplified region's bounding box will be
* the same as for the current region.
*/
void SimplifyOutward (uint32_t aMaxRects);
/**
* Simplify the region by adding at most aThreshold area between spans of
* rects. The simplified region will be a superset of the original region.
* The simplified region's bounding box will be the same as for the current
* region.
*/
void SimplifyOutwardByArea(uint32_t aThreshold);
/**
* Make sure the region has at most aMaxRects by removing area from
* it if necessary. The simplified region will be a subset of the
* original region.
*/
void SimplifyInward (uint32_t aMaxRects);
/**
* VisitEdges is a weird kind of function that we use for padding
* out surfaces to prevent texture filtering artifacts.
* It calls the visitFn callback for each of the exterior edges of
* the regions. The top and bottom edges will be expanded 1 pixel
* to the left and right if there's an outside corner. The order
* the edges are visited is not guaranteed.
*
* visitFn has a side parameter that can be TOP,BOTTOM,LEFT,RIGHT
* and specifies which kind of edge is being visited. x1, y1, x2, y2
* are the coordinates of the line. (x1 == x2) || (y1 == y2)
*/
typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
void VisitEdges(visitFn, void *closure);
nsCString ToString() const;
class RectIterator
{
int mCurrent; // Index of the current entry
int mLimit; // Index one past the final entry.
mutable nsRect mTmp; // The most recently gotten rectangle.
pixman_box32_t *mBoxes;
public:
explicit RectIterator(const nsRegion& aRegion)
{
mCurrent = 0;
mBoxes = pixman_region32_rectangles(aRegion.Impl(), &mLimit);
// Work around pixman bug. Sometimes pixman creates regions with 1 rect
// that's empty.
if (mLimit == 1 && nsRegion::BoxToRect(mBoxes[0]).IsEmpty()) {
mLimit = 0;
}
}
bool Done() const { return mCurrent == mLimit; }
const nsRect& Get() const
{
MOZ_ASSERT(!Done());
mTmp = nsRegion::BoxToRect(mBoxes[mCurrent]);
NS_ASSERTION(!mTmp.IsEmpty(), "Shouldn't return empty rect");
return mTmp;
}
void Next()
{
MOZ_ASSERT(!Done());
mCurrent++;
}
};
RectIterator RectIter() const { return RectIterator(*this); }
private:
pixman_region32_t mImpl;
#ifndef MOZ_TREE_PIXMAN
// For compatibility with pixman versions older than 0.25.2.
static inline void
pixman_region32_clear(pixman_region32_t *region)
{
pixman_region32_fini(region);
pixman_region32_init(region);
}
#endif
nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const;
nsRegion& Copy (const nsRegion& aRegion)
{
pixman_region32_copy(&mImpl, aRegion.Impl());
return *this;
}
nsRegion& Copy (const nsRect& aRect)
{
// pixman needs to distinguish between an empty region and a region
// with one rect so that it can return a different number of rectangles.
// Empty rect: data = empty_box
// 1 rect: data = null
// >1 rect: data = rects
if (aRect.IsEmpty()) {
pixman_region32_clear(&mImpl);
} else {
pixman_box32_t box = RectToBox(aRect);
pixman_region32_reset(&mImpl, &box);
}
return *this;
}
static inline pixman_box32_t RectToBox(const nsRect &aRect)
{
pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
return box;
}
static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect)
{
pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
return box;
}
static inline nsRect BoxToRect(const pixman_box32_t &aBox)
{
return nsRect(aBox.x1, aBox.y1,
aBox.x2 - aBox.x1,
aBox.y2 - aBox.y1);
}
pixman_region32_t* Impl() const
{
return const_cast<pixman_region32_t*>(&mImpl);
}
};
namespace mozilla {
namespace gfx {
/**
* BaseIntRegions use int32_t coordinates.
*/
template <typename Derived, typename Rect, typename Point, typename Margin>
class BaseIntRegion
{
friend class ::nsRegion;
// Give access to all specializations of IntRegionTyped, not just ones that
// derive from this specialization of BaseIntRegion.
template <typename units>
friend class IntRegionTyped;
public:
typedef Rect RectType;
typedef Point PointType;
typedef Margin MarginType;
BaseIntRegion () {}
MOZ_IMPLICIT BaseIntRegion (const Rect& aRect) : mImpl (ToRect(aRect)) {}
explicit BaseIntRegion (mozilla::gfx::ArrayView<pixman_box32_t> aRects) : mImpl (aRects) {}
BaseIntRegion (const BaseIntRegion& aRegion) : mImpl (aRegion.mImpl) {}
BaseIntRegion (BaseIntRegion&& aRegion) : mImpl (mozilla::Move(aRegion.mImpl)) {}
Derived& operator = (const Rect& aRect) { mImpl = ToRect (aRect); return This(); }
Derived& operator = (const Derived& aRegion) { mImpl = aRegion.mImpl; return This(); }
Derived& operator = (Derived&& aRegion) { mImpl = mozilla::Move(aRegion.mImpl); return This(); }
bool operator==(const Derived& aRgn) const
{
return IsEqual(aRgn);
}
bool operator!=(const Derived& aRgn) const
{
return !(*this == aRgn);
}
friend std::ostream& operator<<(std::ostream& stream, const Derived& m) {
return stream << m.mImpl;
}
void Swap(Derived* aOther)
{
mImpl.Swap(&aOther->mImpl);
}
void AndWith(const Derived& aOther)
{
And(This(), aOther);
}
void AndWith(const Rect& aOther)
{
And(This(), aOther);
}
Derived& And (const Derived& aRgn1, const Derived& aRgn2)
{
mImpl.And (aRgn1.mImpl, aRgn2.mImpl);
return This();
}
Derived& And (const Derived& aRegion, const Rect& aRect)
{
mImpl.And (aRegion.mImpl, ToRect (aRect));
return This();
}
Derived& And (const Rect& aRect, const Derived& aRegion)
{
return And (aRegion, aRect);
}
Derived& And (const Rect& aRect1, const Rect& aRect2)
{
Rect TmpRect;
TmpRect.IntersectRect (aRect1, aRect2);
mImpl = ToRect (TmpRect);
return This();
}
Derived& OrWith(const Derived& aOther)
{
return Or(This(), aOther);
}
Derived& OrWith(const Rect& aOther)
{
return Or(This(), aOther);
}
Derived& Or (const Derived& aRgn1, const Derived& aRgn2)
{
mImpl.Or (aRgn1.mImpl, aRgn2.mImpl);
return This();
}
Derived& Or (const Derived& aRegion, const Rect& aRect)
{
mImpl.Or (aRegion.mImpl, ToRect (aRect));
return This();
}
Derived& Or (const Rect& aRect, const Derived& aRegion)
{
return Or (aRegion, aRect);
}
Derived& Or (const Rect& aRect1, const Rect& aRect2)
{
mImpl = ToRect (aRect1);
return Or (This(), aRect2);
}
Derived& XorWith(const Derived& aOther)
{
return Xor(This(), aOther);
}
Derived& XorWith(const Rect& aOther)
{
return Xor(This(), aOther);
}
Derived& Xor (const Derived& aRgn1, const Derived& aRgn2)
{
mImpl.Xor (aRgn1.mImpl, aRgn2.mImpl);
return This();
}
Derived& Xor (const Derived& aRegion, const Rect& aRect)
{
mImpl.Xor (aRegion.mImpl, ToRect (aRect));
return This();
}
Derived& Xor (const Rect& aRect, const Derived& aRegion)
{
return Xor (aRegion, aRect);
}
Derived& Xor (const Rect& aRect1, const Rect& aRect2)
{
mImpl = ToRect (aRect1);
return Xor (This(), aRect2);
}
Derived& SubOut(const Derived& aOther)
{
return Sub(This(), aOther);
}
Derived& SubOut(const Rect& aOther)
{
return Sub(This(), aOther);
}
Derived& Sub (const Derived& aRgn1, const Derived& aRgn2)
{
mImpl.Sub (aRgn1.mImpl, aRgn2.mImpl);
return This();
}
Derived& Sub (const Derived& aRegion, const Rect& aRect)
{
mImpl.Sub (aRegion.mImpl, ToRect (aRect));
return This();
}
Derived& Sub (const Rect& aRect, const Derived& aRegion)
{
return Sub (Derived (aRect), aRegion);
}
Derived& Sub (const Rect& aRect1, const Rect& aRect2)
{
mImpl = ToRect (aRect1);
return Sub (This(), aRect2);
}
/**
* Returns true iff the given point is inside the region. A region
* created from a rect (x=0, y=0, w=100, h=100) will NOT contain
* the point x=100, y=100.
*/
bool Contains (int aX, int aY) const
{
return mImpl.Contains(aX, aY);
}
bool Contains (const Rect& aRect) const
{
return mImpl.Contains (ToRect (aRect));
}
bool Contains (const Derived& aRgn) const
{
return mImpl.Contains (aRgn.mImpl);
}
bool Intersects (const Rect& aRect) const
{
return mImpl.Intersects (ToRect (aRect));
}
void MoveBy (int32_t aXOffset, int32_t aYOffset)
{
MoveBy (Point (aXOffset, aYOffset));
}
void MoveBy (Point aPt)
{
mImpl.MoveBy (aPt.x, aPt.y);
}
Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const
{
return MovedBy(Point(aXOffset, aYOffset));
}
Derived MovedBy(const Point& aPt) const
{
Derived copy(This());
copy.MoveBy(aPt);
return copy;
}
Derived Intersect(const Derived& aOther) const
{
Derived intersection;
intersection.And(This(), aOther);
return intersection;
}
void Inflate(const Margin& aMargin)
{
mImpl.Inflate(nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left));
}
Derived Inflated(const Margin& aMargin) const
{
Derived copy(This());
copy.Inflate(aMargin);
return copy;
}
void SetEmpty ()
{
mImpl.SetEmpty ();
}
bool IsEmpty () const { return mImpl.IsEmpty (); }
bool IsComplex () const { return mImpl.IsComplex (); }
bool IsEqual (const Derived& aRegion) const
{
return mImpl.IsEqual (aRegion.mImpl);
}
uint32_t GetNumRects () const { return mImpl.GetNumRects (); }
Rect GetBounds () const { return FromRect (mImpl.GetBounds ()); }
uint64_t Area () const { return mImpl.Area(); }
nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const
{
nsRegion result;
for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel);
result.Or(result, appRect);
}
return result;
}
Rect GetLargestRectangle (const Rect& aContainingRect = Rect()) const
{
return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) ));
}
Derived& ScaleRoundOut (float aXScale, float aYScale)
{
mImpl.ScaleRoundOut(aXScale, aYScale);
return This();
}
Derived& ScaleInverseRoundOut (float aXScale, float aYScale)
{
mImpl.ScaleInverseRoundOut(aXScale, aYScale);
return This();
}
// Prefer using TransformBy(matrix, region) from UnitTransforms.h,
// as applying the transform should typically change the unit system.
// TODO(botond): Move this to IntRegionTyped and disable it for
// unit != UnknownUnits.
Derived& Transform (const mozilla::gfx::Matrix4x4 &aTransform)
{
mImpl.Transform(aTransform);
return This();
}
/**
* Make sure the region has at most aMaxRects by adding area to it
* if necessary. The simplified region will be a superset of the
* original region. The simplified region's bounding box will be
* the same as for the current region.
*/
void SimplifyOutward (uint32_t aMaxRects)
{
mImpl.SimplifyOutward (aMaxRects);
}
void SimplifyOutwardByArea (uint32_t aThreshold)
{
mImpl.SimplifyOutwardByArea (aThreshold);
}
/**
* Make sure the region has at most aMaxRects by removing area from
* it if necessary. The simplified region will be a subset of the
* original region.
*/
void SimplifyInward (uint32_t aMaxRects)
{
mImpl.SimplifyInward (aMaxRects);
}
typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
void VisitEdges (visitFn visit, void *closure)
{
mImpl.VisitEdges (visit, closure);
}
nsCString ToString() const { return mImpl.ToString(); }
class RectIterator
{
nsRegion::RectIterator mImpl; // The underlying iterator.
mutable Rect mTmp; // The most recently gotten rectangle.
public:
explicit RectIterator(const BaseIntRegion& aRegion)
: mImpl(aRegion.mImpl)
{}
bool Done() const { return mImpl.Done(); }
const Rect& Get() const
{
mTmp = FromRect(mImpl.Get());
return mTmp;
}
void Next() { mImpl.Next(); }
};
RectIterator RectIter() const { return RectIterator(*this); }
protected:
// Expose enough to derived classes from them to define conversions
// between different types of BaseIntRegions.
explicit BaseIntRegion(const nsRegion& aImpl) : mImpl(aImpl) {}
const nsRegion& Impl() const { return mImpl; }
private:
nsRegion mImpl;
static nsRect ToRect(const Rect& aRect)
{
return nsRect (aRect.x, aRect.y, aRect.width, aRect.height);
}
static Rect FromRect(const nsRect& aRect)
{
return Rect (aRect.x, aRect.y, aRect.width, aRect.height);
}
Derived& This()
{
return *static_cast<Derived*>(this);
}
const Derived& This() const
{
return *static_cast<const Derived*>(this);
}
};
template <class units>
class IntRegionTyped :
public BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, IntPointTyped<units>, IntMarginTyped<units>>
{
typedef BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, IntPointTyped<units>, IntMarginTyped<units>> Super;
// Make other specializations of IntRegionTyped friends.
template <typename OtherUnits>
friend class IntRegionTyped;
static_assert(IsPixel<units>::value, "'units' must be a coordinate system tag");
public:
typedef IntRectTyped<units> RectType;
typedef IntPointTyped<units> PointType;
typedef IntMarginTyped<units> MarginType;
// Forward constructors.
IntRegionTyped() {}
MOZ_IMPLICIT IntRegionTyped(const IntRectTyped<units>& aRect) : Super(aRect) {}
IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {}
explicit IntRegionTyped(mozilla::gfx::ArrayView<pixman_box32_t> aRects) : Super(aRects) {}
IntRegionTyped(IntRegionTyped&& aRegion) : Super(mozilla::Move(aRegion)) {}
// Assignment operators need to be forwarded as well, otherwise the compiler
// will declare deleted ones.
IntRegionTyped& operator=(const IntRegionTyped& aRegion)
{
return Super::operator=(aRegion);
}
IntRegionTyped& operator=(IntRegionTyped&& aRegion)
{
return Super::operator=(mozilla::Move(aRegion));
}
static IntRegionTyped FromUnknownRegion(const IntRegion& aRegion)
{
return IntRegionTyped(aRegion.Impl());
}
IntRegion ToUnknownRegion() const
{
// Need |this->| because Impl() is defined in a dependent base class.
return IntRegion(this->Impl());
}
private:
// This is deliberately private, so calling code uses FromUnknownRegion().
explicit IntRegionTyped(const nsRegion& aRegion) : Super(aRegion) {}
};
} // namespace gfx
} // namespace mozilla
typedef mozilla::gfx::IntRegion nsIntRegion;
#endif

26
gfx/src/nsRegionFwd.h Normal file
View file

@ -0,0 +1,26 @@
/* 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/. */
#ifndef nsRegionFwd_h__
#define nsRegionFwd_h__
// Forward declare enough things to define the typedef |nsIntRegion|.
namespace mozilla {
namespace gfx {
struct UnknownUnits;
template <class units>
class IntRegionTyped;
typedef IntRegionTyped<UnknownUnits> IntRegion;
} // namespace gfx
} // namespace mozilla
typedef mozilla::gfx::IntRegion nsIntRegion;
#endif

View file

@ -0,0 +1,41 @@
/* -*- 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/. */
#ifndef NSRENDERINGCONTEXT__H__
#define NSRENDERINGCONTEXT__H__
#include "gfxContext.h"
#include "mozilla/Attributes.h"
#include "nsCOMPtr.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
namespace gfx {
class DrawTarget;
} // namespace gfx
} // namespace mozilla
class MOZ_STACK_CLASS nsRenderingContext final
{
typedef mozilla::gfx::DrawTarget DrawTarget;
public:
explicit nsRenderingContext(gfxContext* aThebesContext)
: mThebes(aThebesContext)
{}
explicit nsRenderingContext(already_AddRefed<gfxContext>&& aThebesContext)
: mThebes(aThebesContext)
{}
// These accessors will never return null.
gfxContext *ThebesContext() { return mThebes; }
DrawTarget *GetDrawTarget() { return mThebes->GetDrawTarget(); }
private:
RefPtr<gfxContext> mThebes;
};
#endif // NSRENDERINGCONTEXT__H__

View file

@ -0,0 +1,159 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsScriptableRegion.h"
#include <stdint.h> // for uint32_t
#include <sys/types.h> // for int32_t
#include "js/RootingAPI.h" // for Rooted
#include "js/Value.h" // for INT_TO_JSVAL, etc
#include "jsapi.h" // for JS_DefineElement, etc
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc
#include "nsID.h"
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nscore.h" // for NS_IMETHODIMP
class JSObject;
struct JSContext;
nsScriptableRegion::nsScriptableRegion()
{
}
NS_IMPL_ISUPPORTS(nsScriptableRegion, nsIScriptableRegion)
NS_IMETHODIMP nsScriptableRegion::Init()
{
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::SetToRegion(nsIScriptableRegion *aRegion)
{
aRegion->GetRegion(&mRegion);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::SetToRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
{
mRegion = mozilla::gfx::IntRect(aX, aY, aWidth, aHeight);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::IntersectRegion(nsIScriptableRegion *aRegion)
{
nsIntRegion region;
aRegion->GetRegion(&region);
mRegion.And(mRegion, region);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::IntersectRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
{
mRegion.And(mRegion, mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::UnionRegion(nsIScriptableRegion *aRegion)
{
nsIntRegion region;
aRegion->GetRegion(&region);
mRegion.Or(mRegion, region);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::UnionRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
{
mRegion.Or(mRegion, mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::SubtractRegion(nsIScriptableRegion *aRegion)
{
nsIntRegion region;
aRegion->GetRegion(&region);
mRegion.Sub(mRegion, region);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::SubtractRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
{
mRegion.Sub(mRegion, mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::IsEmpty(bool *isEmpty)
{
*isEmpty = mRegion.IsEmpty();
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::IsEqualRegion(nsIScriptableRegion *aRegion, bool *isEqual)
{
nsIntRegion region;
aRegion->GetRegion(&region);
*isEqual = mRegion.IsEqual(region);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::GetBoundingBox(int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight)
{
mozilla::gfx::IntRect boundRect = mRegion.GetBounds();
*aX = boundRect.x;
*aY = boundRect.y;
*aWidth = boundRect.width;
*aHeight = boundRect.height;
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::Offset(int32_t aXOffset, int32_t aYOffset)
{
mRegion.MoveBy(aXOffset, aYOffset);
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::ContainsRect(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool *containsRect)
{
*containsRect = mRegion.Contains(mozilla::gfx::IntRect(aX, aY, aWidth, aHeight));
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::GetRegion(nsIntRegion* outRgn)
{
*outRgn = mRegion;
return NS_OK;
}
NS_IMETHODIMP nsScriptableRegion::GetRects(JSContext* aCx, JS::MutableHandle<JS::Value> aRects)
{
uint32_t numRects = mRegion.GetNumRects();
if (!numRects) {
aRects.setNull();
return NS_OK;
}
JS::Rooted<JSObject*> destArray(aCx, JS_NewArrayObject(aCx, numRects * 4));
if (!destArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
aRects.setObject(*destArray);
uint32_t n = 0;
for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
const mozilla::gfx::IntRect& rect = iter.Get();
if (!JS_DefineElement(aCx, destArray, n, rect.x, JSPROP_ENUMERATE) ||
!JS_DefineElement(aCx, destArray, n + 1, rect.y, JSPROP_ENUMERATE) ||
!JS_DefineElement(aCx, destArray, n + 2, rect.width, JSPROP_ENUMERATE) ||
!JS_DefineElement(aCx, destArray, n + 3, rect.height, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
n += 4;
}
return NS_OK;
}

View file

@ -0,0 +1,28 @@
/* -*- Mode: IDL; tab-width: 2; 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/. */
#ifndef nsScriptableRegion_h
#define nsScriptableRegion_h
#include "nsIScriptableRegion.h"
#include "nsISupports.h"
#include "nsRegion.h"
#include "mozilla/Attributes.h"
class nsScriptableRegion final : public nsIScriptableRegion {
public:
nsScriptableRegion();
NS_DECL_ISUPPORTS
NS_DECL_NSISCRIPTABLEREGION
private:
~nsScriptableRegion() {}
nsIntRegion mRegion;
};
#endif

70
gfx/src/nsSize.h Normal file
View file

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef NSSIZE_H
#define NSSIZE_H
#include "nsCoord.h"
#include "mozilla/gfx/BaseSize.h"
#include "mozilla/gfx/Point.h"
// Maximum allowable size
#define NS_MAXSIZE nscoord_MAX
typedef mozilla::gfx::IntSize nsIntSize;
struct nsSize : public mozilla::gfx::BaseSize<nscoord, nsSize> {
typedef mozilla::gfx::BaseSize<nscoord, nsSize> Super;
nsSize() : Super() {}
nsSize(nscoord aWidth, nscoord aHeight) : Super(aWidth, aHeight) {}
inline mozilla::gfx::IntSize ScaleToNearestPixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const;
inline mozilla::gfx::IntSize ToNearestPixels(nscoord aAppUnitsPerPixel) const;
/**
* Return this size scaled to a different appunits per pixel (APP) ratio.
* @param aFromAPP the APP to scale from
* @param aToAPP the APP to scale to
*/
MOZ_MUST_USE inline nsSize
ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const;
};
inline mozilla::gfx::IntSize
nsSize::ScaleToNearestPixels(float aXScale, float aYScale,
nscoord aAppUnitsPerPixel) const
{
return mozilla::gfx::IntSize(
NSToIntRoundUp(NSAppUnitsToDoublePixels(width, aAppUnitsPerPixel) * aXScale),
NSToIntRoundUp(NSAppUnitsToDoublePixels(height, aAppUnitsPerPixel) * aYScale));
}
inline mozilla::gfx::IntSize
nsSize::ToNearestPixels(nscoord aAppUnitsPerPixel) const
{
return ScaleToNearestPixels(1.0f, 1.0f, aAppUnitsPerPixel);
}
inline nsSize
nsSize::ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const {
if (aFromAPP != aToAPP) {
nsSize size;
size.width = NSToCoordRound(NSCoordScale(width, aFromAPP, aToAPP));
size.height = NSToCoordRound(NSCoordScale(height, aFromAPP, aToAPP));
return size;
}
return *this;
}
inline nsSize
IntSizeToAppUnits(mozilla::gfx::IntSize aSize, nscoord aAppUnitsPerPixel)
{
return nsSize(NSIntPixelsToAppUnits(aSize.width, aAppUnitsPerPixel),
NSIntPixelsToAppUnits(aSize.height, aAppUnitsPerPixel));
}
#endif /* NSSIZE_H */

View file

@ -0,0 +1,129 @@
/* -*- 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 "nsThebesFontEnumerator.h"
#include <stdint.h> // for uint32_t
#include "gfxPlatform.h" // for gfxPlatform
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsDebug.h" // for NS_ENSURE_ARG_POINTER
#include "nsError.h" // for NS_OK, NS_FAILED, nsresult
#include "nsIAtom.h" // for nsIAtom, NS_Atomize
#include "nsID.h"
#include "nsMemory.h" // for nsMemory
#include "nsString.h" // for nsAutoCString, nsAutoString, etc
#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
#include "nscore.h" // for char16_t, NS_IMETHODIMP
NS_IMPL_ISUPPORTS(nsThebesFontEnumerator, nsIFontEnumerator)
nsThebesFontEnumerator::nsThebesFontEnumerator()
{
}
NS_IMETHODIMP
nsThebesFontEnumerator::EnumerateAllFonts(uint32_t *aCount,
char16_t ***aResult)
{
return EnumerateFonts (nullptr, nullptr, aCount, aResult);
}
NS_IMETHODIMP
nsThebesFontEnumerator::EnumerateFonts(const char *aLangGroup,
const char *aGeneric,
uint32_t *aCount,
char16_t ***aResult)
{
NS_ENSURE_ARG_POINTER(aCount);
NS_ENSURE_ARG_POINTER(aResult);
nsTArray<nsString> fontList;
nsAutoCString generic;
if (aGeneric)
generic.Assign(aGeneric);
else
generic.SetIsVoid(true);
nsCOMPtr<nsIAtom> langGroupAtom;
if (aLangGroup) {
nsAutoCString lowered;
lowered.Assign(aLangGroup);
ToLowerCase(lowered);
langGroupAtom = NS_Atomize(lowered);
}
nsresult rv = gfxPlatform::GetPlatform()->GetFontList(langGroupAtom, generic, fontList);
if (NS_FAILED(rv)) {
*aCount = 0;
*aResult = nullptr;
/* XXX in this case, do we want to return the CSS generics? */
return NS_OK;
}
char16_t **fs = static_cast<char16_t **>
(moz_xmalloc(fontList.Length() * sizeof(char16_t*)));
for (uint32_t i = 0; i < fontList.Length(); i++) {
fs[i] = ToNewUnicode(fontList[i]);
}
*aResult = fs;
*aCount = fontList.Length();
return NS_OK;
}
NS_IMETHODIMP
nsThebesFontEnumerator::HaveFontFor(const char *aLangGroup,
bool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = true;
return NS_OK;
}
NS_IMETHODIMP
nsThebesFontEnumerator::GetDefaultFont(const char *aLangGroup,
const char *aGeneric,
char16_t **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsThebesFontEnumerator::UpdateFontList(bool *_retval)
{
gfxPlatform::GetPlatform()->UpdateFontList();
*_retval = false; // always return false for now
return NS_OK;
}
NS_IMETHODIMP
nsThebesFontEnumerator::GetStandardFamilyName(const char16_t *aName,
char16_t **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(aName);
nsAutoString name(aName);
if (name.IsEmpty()) {
*aResult = nullptr;
return NS_OK;
}
nsAutoString family;
nsresult rv = gfxPlatform::GetPlatform()->
GetStandardFamilyName(nsDependentString(aName), family);
if (NS_FAILED(rv) || family.IsEmpty()) {
*aResult = nullptr;
return NS_OK;
}
*aResult = ToNewUnicode(family);
return NS_OK;
}

View file

@ -0,0 +1,24 @@
/* -*- 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/. */
#ifndef _NSTHEBESFONTENUMERATOR_H_
#define _NSTHEBESFONTENUMERATOR_H_
#include "mozilla/Attributes.h" // for final
#include "nsIFontEnumerator.h" // for NS_DECL_NSIFONTENUMERATOR, etc
#include "nsISupports.h" // for NS_DECL_ISUPPORTS
class nsThebesFontEnumerator final : public nsIFontEnumerator
{
~nsThebesFontEnumerator() {}
public:
nsThebesFontEnumerator();
NS_DECL_ISUPPORTS
NS_DECL_NSIFONTENUMERATOR
};
#endif /* _NSTHEBESFONTENUMERATOR_H_ */

View file

@ -0,0 +1,63 @@
/* -*- 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 "gfxPlatform.h" // for gfxPlatform
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/Attributes.h" // for final
#include "mozilla/Module.h" // for Module, Module::CIDEntry, etc
#include "mozilla/ModuleUtils.h"
#include "mozilla/mozalloc.h" // for operator new
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsError.h" // for NS_ERROR_NO_AGGREGATION, etc
#include "nsGfxCIID.h" // for NS_FONT_ENUMERATOR_CID, etc
#include "nsID.h" // for NS_DEFINE_NAMED_CID, etc
#include "nsIScriptableRegion.h" // for nsIScriptableRegion
#include "nsISupports.h" // for NS_DECL_ISUPPORTS, etc
#include "nsScriptableRegion.h" // for nsScriptableRegion
#include "nsThebesFontEnumerator.h" // for nsThebesFontEnumerator
NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesFontEnumerator)
static nsresult
nsScriptableRegionConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
if (!aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = nullptr;
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
nsCOMPtr<nsIScriptableRegion> scriptableRgn = new nsScriptableRegion();
return scriptableRgn->QueryInterface(aIID, aResult);
}
NS_DEFINE_NAMED_CID(NS_FONT_ENUMERATOR_CID);
NS_DEFINE_NAMED_CID(NS_SCRIPTABLE_REGION_CID);
static const mozilla::Module::CIDEntry kThebesCIDs[] = {
{ &kNS_FONT_ENUMERATOR_CID, false, nullptr, nsThebesFontEnumeratorConstructor },
{ &kNS_SCRIPTABLE_REGION_CID, false, nullptr, nsScriptableRegionConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kThebesContracts[] = {
{ "@mozilla.org/gfx/fontenumerator;1", &kNS_FONT_ENUMERATOR_CID },
{ "@mozilla.org/gfx/region;1", &kNS_SCRIPTABLE_REGION_CID },
{ nullptr }
};
static const mozilla::Module kThebesModule = {
mozilla::Module::kVersion,
kThebesCIDs,
kThebesContracts,
nullptr,
nullptr,
nullptr,
nullptr
};
NSMODULE_DEFN(nsGfxModule) = &kThebesModule;

296
gfx/src/nsThemeConstants.h Normal file
View file

@ -0,0 +1,296 @@
/* 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/. */
// No appearance at all.
#define NS_THEME_NONE 0
// A typical dialog button.
#define NS_THEME_BUTTON 1
// A radio element within a radio group.
#define NS_THEME_RADIO 2
// A checkbox element.
#define NS_THEME_CHECKBOX 3
// A rectangular button that contains complex content
// like images (e.g. HTML <button> elements)
#define NS_THEME_BUTTON_BEVEL 7
// A themed focus outline (for outline:auto)
#define NS_THEME_FOCUS_OUTLINE 8
// The toolbox that contains the toolbars.
#define NS_THEME_TOOLBOX 11
// A toolbar in an application window.
#define NS_THEME_TOOLBAR 12
// A single toolbar button (with no associated dropdown)
#define NS_THEME_TOOLBARBUTTON 13
// A dual toolbar button (e.g., a Back button with a dropdown)
#define NS_THEME_DUALBUTTON 14
// The dropdown portion of a toolbar button
#define NS_THEME_TOOLBARBUTTON_DROPDOWN 15
// Various arrows that go in buttons
#define NS_THEME_BUTTON_ARROW_UP 16
#define NS_THEME_BUTTON_ARROW_DOWN 17
#define NS_THEME_BUTTON_ARROW_NEXT 18
#define NS_THEME_BUTTON_ARROW_PREVIOUS 19
// A separator. Can be horizontal or vertical.
#define NS_THEME_SEPARATOR 20
// The gripper for a toolbar.
#define NS_THEME_TOOLBARGRIPPER 21
// A splitter. Can be horizontal or vertical.
#define NS_THEME_SPLITTER 22
// A status bar in a main application window.
#define NS_THEME_STATUSBAR 23
// A single pane of a status bar.
#define NS_THEME_STATUSBARPANEL 24
// The resizer background area in a status bar
// for the resizer widget in the corner of a window.
#define NS_THEME_RESIZERPANEL 25
// The resizer itself.
#define NS_THEME_RESIZER 26
// List boxes
#define NS_THEME_LISTBOX 31
// A listbox item
#define NS_THEME_LISTITEM 32
// A tree widget
#define NS_THEME_TREEVIEW 41
// A tree item
#define NS_THEME_TREEITEM 42
// A tree widget twisty
#define NS_THEME_TREETWISTY 43
// A tree widget branch line
#define NS_THEME_TREELINE 44
// A listbox or tree widget header
#define NS_THEME_TREEHEADER 45
// An individual header cell
#define NS_THEME_TREEHEADERCELL 46
// The sort arrow for a header.
#define NS_THEME_TREEHEADERSORTARROW 47
// Open tree widget twisty
#define NS_THEME_TREETWISTYOPEN 48
// A horizontal progress bar.
#define NS_THEME_PROGRESSBAR 51
// The progress bar's progress indicator
#define NS_THEME_PROGRESSCHUNK 52
// A vertical progress bar.
#define NS_THEME_PROGRESSBAR_VERTICAL 53
// A vertical progress chunk
#define NS_THEME_PROGRESSCHUNK_VERTICAL 54
// A horizontal meter bar.
#define NS_THEME_METERBAR 55
// The meter bar's meter indicator
#define NS_THEME_METERCHUNK 56
// A single tab in a tab widget.
#define NS_THEME_TAB 61
// A single pane (inside the tabpanels container)
#define NS_THEME_TABPANEL 62
// The tab panels container.
#define NS_THEME_TABPANELS 65
// The tabs scroll arrows (left/right)
#define NS_THEME_TAB_SCROLL_ARROW_BACK 66
#define NS_THEME_TAB_SCROLL_ARROW_FORWARD 67
// A tooltip
#define NS_THEME_TOOLTIP 71
// A spin control (up/down control for time/date pickers)
#define NS_THEME_SPINNER 72
// The up button of a spin control
#define NS_THEME_SPINNER_UPBUTTON 73
// The down button of a spin control
#define NS_THEME_SPINNER_DOWNBUTTON 74
// The textfield of a spin control
#define NS_THEME_SPINNER_TEXTFIELD 75
// For HTML's <input type=number>
#define NS_THEME_NUMBER_INPUT 76
// A scrollbar.
#define NS_THEME_SCROLLBAR 80
// A small scrollbar.
#define NS_THEME_SCROLLBAR_SMALL 81
// The scrollbar slider
#define NS_THEME_SCROLLBAR_HORIZONTAL 82
#define NS_THEME_SCROLLBAR_VERTICAL 83
// A scrollbar button (up/down/left/right)
#define NS_THEME_SCROLLBARBUTTON_UP 84
#define NS_THEME_SCROLLBARBUTTON_DOWN 85
#define NS_THEME_SCROLLBARBUTTON_LEFT 86
#define NS_THEME_SCROLLBARBUTTON_RIGHT 87
// The scrollbar track
#define NS_THEME_SCROLLBARTRACK_HORIZONTAL 88
#define NS_THEME_SCROLLBARTRACK_VERTICAL 89
// The scrollbar thumb
#define NS_THEME_SCROLLBARTHUMB_HORIZONTAL 90
#define NS_THEME_SCROLLBARTHUMB_VERTICAL 91
// A non-disappearing scrollbar.
#define NS_THEME_SCROLLBAR_NON_DISAPPEARING 92
// A textfield or text area
#define NS_THEME_TEXTFIELD 95
// The caret of a text area
#define NS_THEME_CARET 96
// A multiline text field
#define NS_THEME_TEXTFIELD_MULTILINE 97
// A searchfield
#define NS_THEME_SEARCHFIELD 98
// A dropdown list.
#define NS_THEME_MENULIST 101
// The dropdown button(s) that open up a dropdown list.
#define NS_THEME_MENULIST_BUTTON 102
// The text part of a dropdown list, to left of button
#define NS_THEME_MENULIST_TEXT 103
// An editable textfield with a dropdown list (a combobox)
#define NS_THEME_MENULIST_TEXTFIELD 104
// A slider
#define NS_THEME_SCALE_HORIZONTAL 111
#define NS_THEME_SCALE_VERTICAL 112
// A slider's thumb
#define NS_THEME_SCALETHUMB_HORIZONTAL 113
#define NS_THEME_SCALETHUMB_VERTICAL 114
// If the platform supports it, the left/right chunks
// of the slider thumb
#define NS_THEME_SCALETHUMBSTART 115
#define NS_THEME_SCALETHUMBEND 116
// The ticks for a slider.
#define NS_THEME_SCALETHUMBTICK 117
// nsRangeFrame and its subparts
#define NS_THEME_RANGE 120
#define NS_THEME_RANGE_THUMB 121
// A groupbox
#define NS_THEME_GROUPBOX 149
// A generic container that always repaints on state
// changes. This is a hack to make checkboxes and
// radio buttons work.
#define NS_THEME_CHECKBOX_CONTAINER 150
#define NS_THEME_RADIO_CONTAINER 151
// The label part of a checkbox or radio button, used for painting
// a focus outline.
#define NS_THEME_CHECKBOX_LABEL 152
#define NS_THEME_RADIO_LABEL 153
// The focus outline box inside of a button
#define NS_THEME_BUTTON_FOCUS 154
// Window and dialog backgrounds
#define NS_THEME_WINDOW 200
#define NS_THEME_DIALOG 201
// Menu Bar background
#define NS_THEME_MENUBAR 210
// Menu Popup background
#define NS_THEME_MENUPOPUP 211
// <menu> and <menuitem> appearances
#define NS_THEME_MENUITEM 212
#define NS_THEME_CHECKMENUITEM 213
#define NS_THEME_RADIOMENUITEM 214
// menu checkbox/radio appearances
#define NS_THEME_MENUCHECKBOX 215
#define NS_THEME_MENURADIO 216
#define NS_THEME_MENUSEPARATOR 217
#define NS_THEME_MENUARROW 218
// An image in the menu gutter, like in bookmarks or history
#define NS_THEME_MENUIMAGE 219
// For text on non-iconic menuitems only
#define NS_THEME_MENUITEMTEXT 220
// Vista Rebars
#define NS_THEME_WIN_COMMUNICATIONS_TOOLBOX 221
#define NS_THEME_WIN_MEDIA_TOOLBOX 222
#define NS_THEME_WIN_BROWSERTABBAR_TOOLBOX 223
// Titlebar elements on the Mac
#define NS_THEME_MAC_FULLSCREEN_BUTTON 226
// Mac help button
#define NS_THEME_MAC_HELP_BUTTON 227
// Vista glass
#define NS_THEME_WIN_BORDERLESS_GLASS 229
#define NS_THEME_WIN_GLASS 230
// Windows themed window frame elements
#define NS_THEME_WINDOW_TITLEBAR 231
#define NS_THEME_WINDOW_TITLEBAR_MAXIMIZED 232
#define NS_THEME_WINDOW_FRAME_LEFT 233
#define NS_THEME_WINDOW_FRAME_RIGHT 234
#define NS_THEME_WINDOW_FRAME_BOTTOM 235
#define NS_THEME_WINDOW_BUTTON_CLOSE 236
#define NS_THEME_WINDOW_BUTTON_MINIMIZE 237
#define NS_THEME_WINDOW_BUTTON_MAXIMIZE 238
#define NS_THEME_WINDOW_BUTTON_RESTORE 239
#define NS_THEME_WINDOW_BUTTON_BOX 240
#define NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED 241
// moz-apperance style used in setting proper glass margins
#define NS_THEME_WIN_EXCLUDE_GLASS 242
#define NS_THEME_MAC_VIBRANCY_LIGHT 243
#define NS_THEME_MAC_VIBRANCY_DARK 244
#define NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN 245
#define NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED 246
#define NS_THEME_GTK_INFO_BAR 247
#define NS_THEME_MAC_SOURCE_LIST 248
#define NS_THEME_MAC_SOURCE_LIST_SELECTION 249
#define NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION 250

23
gfx/src/nsTransform2D.cpp Normal file
View file

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsTransform2D.h"
void nsTransform2D :: TransformCoord(nscoord *ptX, nscoord *ptY) const
{
*ptX = NSToCoordRound(*ptX * m00 + m20);
*ptY = NSToCoordRound(*ptY * m11 + m21);
}
void nsTransform2D :: TransformCoord(nscoord *aX, nscoord *aY, nscoord *aWidth, nscoord *aHeight) const
{
nscoord x2 = *aX + *aWidth;
nscoord y2 = *aY + *aHeight;
TransformCoord(aX, aY);
TransformCoord(&x2, &y2);
*aWidth = x2 - *aX;
*aHeight = y2 - *aY;
}

86
gfx/src/nsTransform2D.h Normal file
View file

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef nsTransform2D_h___
#define nsTransform2D_h___
#include "nsCoord.h"
class nsTransform2D
{
private:
/**
* This represents the following matrix (note that the order of row/column
* indices is opposite to usual notation)
*
* / m00 0 m20 \
* M = | 0 m11 m21 |
* \ 0 0 1 /
*
* Transformation of a coordinate (x, y) is obtained by setting
* v = (x, y, 1)^T and evaluating M . v
**/
float m00, m11, m20, m21;
public:
nsTransform2D(void) { m20 = m21 = 0.0f; m00 = m11 = 1.0f; }
~nsTransform2D(void) { }
/**
* set this transform to a translation
*
* @param tx, x translation
* @param ty, y translation
**/
void SetToTranslate(float tx, float ty) { m00 = m11 = 1.0f; m20 = tx; m21 = ty; }
/**
* get the translation portion of this transform
*
* @param pt, Point to return translation values in
**/
void GetTranslationCoord(nscoord *ptX, nscoord *ptY) const { *ptX = NSToCoordRound(m20); *ptY = NSToCoordRound(m21); }
/**
* apply matrix to vector
*
* @param pt Point to transform
**/
void TransformCoord(nscoord *ptX, nscoord *ptY) const;
/**
* apply matrix to rect
*
* @param rect Rect to transform
**/
void TransformCoord(nscoord *aX, nscoord *aY, nscoord *aWidth, nscoord *aHeight) const;
/**
* add a scale to a Transform via x, y pair
*
* @param ptX x value to add as x scale
* @param ptY y value to add as y scale
**/
void AddScale(float ptX, float ptY) { m00 *= ptX; m11 *= ptY; }
/**
* Set the scale (overriding any previous calls to AddScale, but leaving
* any existing translation).
*
* @param ptX x value to add as x scale
* @param ptY y value to add as y scale
**/
void SetScale(float ptX, float ptY) { m00 = ptX; m11 = ptY; }
};
#endif