summaryrefslogtreecommitdiffstats
path: root/gfx/src
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/src
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/src')
-rw-r--r--gfx/src/AppUnits.h15
-rw-r--r--gfx/src/ArrayView.h50
-rw-r--r--gfx/src/DriverCrashGuard.cpp620
-rw-r--r--gfx/src/DriverCrashGuard.h182
-rw-r--r--gfx/src/FilterSupport.cpp2185
-rw-r--r--gfx/src/FilterSupport.h479
-rw-r--r--gfx/src/PingPongRegion.h63
-rw-r--r--gfx/src/RegionBuilder.h32
-rw-r--r--gfx/src/TiledRegion.cpp370
-rw-r--r--gfx/src/TiledRegion.h208
-rw-r--r--gfx/src/X11UndefineNone.h23
-rw-r--r--gfx/src/X11Util.cpp94
-rw-r--r--gfx/src/X11Util.h148
-rw-r--r--gfx/src/gfxCrashReporterUtils.cpp145
-rw-r--r--gfx/src/gfxCrashReporterUtils.h51
-rw-r--r--gfx/src/gfxTelemetry.cpp59
-rw-r--r--gfx/src/gfxTelemetry.h70
-rw-r--r--gfx/src/moz.build94
-rw-r--r--gfx/src/nsBoundingMetrics.h87
-rw-r--r--gfx/src/nsColor.cpp361
-rw-r--r--gfx/src/nsColor.h123
-rw-r--r--gfx/src/nsColorNameList.h181
-rw-r--r--gfx/src/nsColorNames.h16
-rw-r--r--gfx/src/nsCoord.h443
-rw-r--r--gfx/src/nsDeviceContext.cpp725
-rw-r--r--gfx/src/nsDeviceContext.h311
-rw-r--r--gfx/src/nsFont.cpp297
-rw-r--r--gfx/src/nsFont.h147
-rw-r--r--gfx/src/nsFontMetrics.cpp445
-rw-r--r--gfx/src/nsFontMetrics.h268
-rw-r--r--gfx/src/nsGfxCIID.h21
-rw-r--r--gfx/src/nsIFontEnumerator.idl61
-rw-r--r--gfx/src/nsIScriptableRegion.idl176
-rw-r--r--gfx/src/nsITheme.h206
-rw-r--r--gfx/src/nsMargin.h26
-rw-r--r--gfx/src/nsPoint.h106
-rw-r--r--gfx/src/nsRect.cpp62
-rw-r--r--gfx/src/nsRect.h324
-rw-r--r--gfx/src/nsRegion.cpp1146
-rw-r--r--gfx/src/nsRegion.h868
-rw-r--r--gfx/src/nsRegionFwd.h26
-rw-r--r--gfx/src/nsRenderingContext.h41
-rw-r--r--gfx/src/nsScriptableRegion.cpp159
-rw-r--r--gfx/src/nsScriptableRegion.h28
-rw-r--r--gfx/src/nsSize.h70
-rw-r--r--gfx/src/nsThebesFontEnumerator.cpp129
-rw-r--r--gfx/src/nsThebesFontEnumerator.h24
-rw-r--r--gfx/src/nsThebesGfxFactory.cpp63
-rw-r--r--gfx/src/nsThemeConstants.h296
-rw-r--r--gfx/src/nsTransform2D.cpp23
-rw-r--r--gfx/src/nsTransform2D.h86
51 files changed, 12233 insertions, 0 deletions
diff --git a/gfx/src/AppUnits.h b/gfx/src/AppUnits.h
new file mode 100644
index 000000000..e0ad6dc52
--- /dev/null
+++ b/gfx/src/AppUnits.h
@@ -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_ */
diff --git a/gfx/src/ArrayView.h b/gfx/src/ArrayView.h
new file mode 100644
index 000000000..3c6704f43
--- /dev/null
+++ b/gfx/src/ArrayView.h
@@ -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_ */
diff --git a/gfx/src/DriverCrashGuard.cpp b/gfx/src/DriverCrashGuard.cpp
new file mode 100644
index 000000000..36d08dcf3
--- /dev/null
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -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
diff --git a/gfx/src/DriverCrashGuard.h b/gfx/src/DriverCrashGuard.h
new file mode 100644
index 000000000..9a0c5851a
--- /dev/null
+++ b/gfx/src/DriverCrashGuard.h
@@ -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__
+
diff --git a/gfx/src/FilterSupport.cpp b/gfx/src/FilterSupport.cpp
new file mode 100644
index 000000000..fed7b6879
--- /dev/null
+++ b/gfx/src/FilterSupport.cpp
@@ -0,0 +1,2185 @@
+/* -*- 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 "FilterSupport.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Filters.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/PodOperations.h"
+
+#include "gfxContext.h"
+#include "gfxPattern.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+
+#include "nsMargin.h"
+
+// c = n / 255
+// c <= 0.0031308f ? c * 12.92f : 1.055f * powf(c, 1 / 2.4f) - 0.055f
+static const float glinearRGBTosRGBMap[256] = {
+ 0.000f, 0.050f, 0.085f, 0.111f, 0.132f, 0.150f, 0.166f, 0.181f,
+ 0.194f, 0.207f, 0.219f, 0.230f, 0.240f, 0.250f, 0.260f, 0.269f,
+ 0.278f, 0.286f, 0.295f, 0.303f, 0.310f, 0.318f, 0.325f, 0.332f,
+ 0.339f, 0.346f, 0.352f, 0.359f, 0.365f, 0.371f, 0.378f, 0.383f,
+ 0.389f, 0.395f, 0.401f, 0.406f, 0.412f, 0.417f, 0.422f, 0.427f,
+ 0.433f, 0.438f, 0.443f, 0.448f, 0.452f, 0.457f, 0.462f, 0.466f,
+ 0.471f, 0.476f, 0.480f, 0.485f, 0.489f, 0.493f, 0.498f, 0.502f,
+ 0.506f, 0.510f, 0.514f, 0.518f, 0.522f, 0.526f, 0.530f, 0.534f,
+ 0.538f, 0.542f, 0.546f, 0.549f, 0.553f, 0.557f, 0.561f, 0.564f,
+ 0.568f, 0.571f, 0.575f, 0.579f, 0.582f, 0.586f, 0.589f, 0.592f,
+ 0.596f, 0.599f, 0.603f, 0.606f, 0.609f, 0.613f, 0.616f, 0.619f,
+ 0.622f, 0.625f, 0.629f, 0.632f, 0.635f, 0.638f, 0.641f, 0.644f,
+ 0.647f, 0.650f, 0.653f, 0.656f, 0.659f, 0.662f, 0.665f, 0.668f,
+ 0.671f, 0.674f, 0.677f, 0.680f, 0.683f, 0.685f, 0.688f, 0.691f,
+ 0.694f, 0.697f, 0.699f, 0.702f, 0.705f, 0.708f, 0.710f, 0.713f,
+ 0.716f, 0.718f, 0.721f, 0.724f, 0.726f, 0.729f, 0.731f, 0.734f,
+ 0.737f, 0.739f, 0.742f, 0.744f, 0.747f, 0.749f, 0.752f, 0.754f,
+ 0.757f, 0.759f, 0.762f, 0.764f, 0.767f, 0.769f, 0.772f, 0.774f,
+ 0.776f, 0.779f, 0.781f, 0.784f, 0.786f, 0.788f, 0.791f, 0.793f,
+ 0.795f, 0.798f, 0.800f, 0.802f, 0.805f, 0.807f, 0.809f, 0.812f,
+ 0.814f, 0.816f, 0.818f, 0.821f, 0.823f, 0.825f, 0.827f, 0.829f,
+ 0.832f, 0.834f, 0.836f, 0.838f, 0.840f, 0.843f, 0.845f, 0.847f,
+ 0.849f, 0.851f, 0.853f, 0.855f, 0.857f, 0.860f, 0.862f, 0.864f,
+ 0.866f, 0.868f, 0.870f, 0.872f, 0.874f, 0.876f, 0.878f, 0.880f,
+ 0.882f, 0.884f, 0.886f, 0.888f, 0.890f, 0.892f, 0.894f, 0.896f,
+ 0.898f, 0.900f, 0.902f, 0.904f, 0.906f, 0.908f, 0.910f, 0.912f,
+ 0.914f, 0.916f, 0.918f, 0.920f, 0.922f, 0.924f, 0.926f, 0.928f,
+ 0.930f, 0.931f, 0.933f, 0.935f, 0.937f, 0.939f, 0.941f, 0.943f,
+ 0.945f, 0.946f, 0.948f, 0.950f, 0.952f, 0.954f, 0.956f, 0.957f,
+ 0.959f, 0.961f, 0.963f, 0.965f, 0.967f, 0.968f, 0.970f, 0.972f,
+ 0.974f, 0.975f, 0.977f, 0.979f, 0.981f, 0.983f, 0.984f, 0.986f,
+ 0.988f, 0.990f, 0.991f, 0.993f, 0.995f, 0.997f, 0.998f, 1.000f
+};
+
+// c = n / 255
+// c <= 0.04045f ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f)
+static const float gsRGBToLinearRGBMap[256] = {
+ 0.000f, 0.000f, 0.001f, 0.001f, 0.001f, 0.002f, 0.002f, 0.002f,
+ 0.002f, 0.003f, 0.003f, 0.003f, 0.004f, 0.004f, 0.004f, 0.005f,
+ 0.005f, 0.006f, 0.006f, 0.007f, 0.007f, 0.007f, 0.008f, 0.009f,
+ 0.009f, 0.010f, 0.010f, 0.011f, 0.012f, 0.012f, 0.013f, 0.014f,
+ 0.014f, 0.015f, 0.016f, 0.017f, 0.018f, 0.019f, 0.019f, 0.020f,
+ 0.021f, 0.022f, 0.023f, 0.024f, 0.025f, 0.026f, 0.027f, 0.028f,
+ 0.030f, 0.031f, 0.032f, 0.033f, 0.034f, 0.036f, 0.037f, 0.038f,
+ 0.040f, 0.041f, 0.042f, 0.044f, 0.045f, 0.047f, 0.048f, 0.050f,
+ 0.051f, 0.053f, 0.054f, 0.056f, 0.058f, 0.060f, 0.061f, 0.063f,
+ 0.065f, 0.067f, 0.068f, 0.070f, 0.072f, 0.074f, 0.076f, 0.078f,
+ 0.080f, 0.082f, 0.084f, 0.087f, 0.089f, 0.091f, 0.093f, 0.095f,
+ 0.098f, 0.100f, 0.102f, 0.105f, 0.107f, 0.109f, 0.112f, 0.114f,
+ 0.117f, 0.120f, 0.122f, 0.125f, 0.127f, 0.130f, 0.133f, 0.136f,
+ 0.138f, 0.141f, 0.144f, 0.147f, 0.150f, 0.153f, 0.156f, 0.159f,
+ 0.162f, 0.165f, 0.168f, 0.171f, 0.175f, 0.178f, 0.181f, 0.184f,
+ 0.188f, 0.191f, 0.195f, 0.198f, 0.202f, 0.205f, 0.209f, 0.212f,
+ 0.216f, 0.220f, 0.223f, 0.227f, 0.231f, 0.235f, 0.238f, 0.242f,
+ 0.246f, 0.250f, 0.254f, 0.258f, 0.262f, 0.266f, 0.270f, 0.275f,
+ 0.279f, 0.283f, 0.287f, 0.292f, 0.296f, 0.301f, 0.305f, 0.309f,
+ 0.314f, 0.319f, 0.323f, 0.328f, 0.332f, 0.337f, 0.342f, 0.347f,
+ 0.352f, 0.356f, 0.361f, 0.366f, 0.371f, 0.376f, 0.381f, 0.386f,
+ 0.392f, 0.397f, 0.402f, 0.407f, 0.413f, 0.418f, 0.423f, 0.429f,
+ 0.434f, 0.440f, 0.445f, 0.451f, 0.456f, 0.462f, 0.468f, 0.474f,
+ 0.479f, 0.485f, 0.491f, 0.497f, 0.503f, 0.509f, 0.515f, 0.521f,
+ 0.527f, 0.533f, 0.539f, 0.546f, 0.552f, 0.558f, 0.565f, 0.571f,
+ 0.578f, 0.584f, 0.591f, 0.597f, 0.604f, 0.610f, 0.617f, 0.624f,
+ 0.631f, 0.638f, 0.644f, 0.651f, 0.658f, 0.665f, 0.672f, 0.680f,
+ 0.687f, 0.694f, 0.701f, 0.708f, 0.716f, 0.723f, 0.730f, 0.738f,
+ 0.745f, 0.753f, 0.761f, 0.768f, 0.776f, 0.784f, 0.791f, 0.799f,
+ 0.807f, 0.815f, 0.823f, 0.831f, 0.839f, 0.847f, 0.855f, 0.863f,
+ 0.871f, 0.880f, 0.888f, 0.896f, 0.905f, 0.913f, 0.922f, 0.930f,
+ 0.939f, 0.947f, 0.956f, 0.965f, 0.973f, 0.982f, 0.991f, 1.000f
+};
+
+namespace mozilla {
+namespace gfx {
+
+// Some convenience FilterNode creation functions.
+
+namespace FilterWrappers {
+
+ static already_AddRefed<FilterNode>
+ Unpremultiply(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY);
+ if (filter) {
+ filter->SetInput(IN_UNPREMULTIPLY_IN, aInput);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Premultiply(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::PREMULTIPLY);
+ if (filter) {
+ filter->SetInput(IN_PREMULTIPLY_IN, aInput);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ LinearRGBToSRGB(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (transfer) {
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, glinearRGBTosRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, glinearRGBTosRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, glinearRGBTosRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
+ transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
+ return transfer.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ SRGBToLinearRGB(DrawTarget* aDT, FilterNode* aInput)
+ {
+ RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (transfer) {
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, gsRGBToLinearRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, gsRGBToLinearRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, gsRGBToLinearRGBMap, 256);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
+ transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
+ return transfer.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Crop(DrawTarget* aDT, FilterNode* aInputFilter, const IntRect& aRect)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CROP);
+ if (filter) {
+ filter->SetAttribute(ATT_CROP_RECT, Rect(aRect));
+ filter->SetInput(IN_CROP_IN, aInputFilter);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Offset(DrawTarget* aDT, FilterNode* aInputFilter, const IntPoint& aOffset)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
+ if (filter) {
+ filter->SetAttribute(ATT_TRANSFORM_MATRIX, Matrix::Translation(aOffset.x, aOffset.y));
+ filter->SetInput(IN_TRANSFORM_IN, aInputFilter);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ GaussianBlur(DrawTarget* aDT, FilterNode* aInputFilter, const Size& aStdDeviation)
+ {
+ float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation));
+ float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation));
+ if (stdX == stdY) {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
+ if (filter) {
+ filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX);
+ filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+ RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
+ RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
+ if (filterH && filterV) {
+ filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X);
+ filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX);
+ filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y);
+ filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
+ filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
+ filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
+ return filterV.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ Clear(DrawTarget* aDT)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
+ if (filter) {
+ filter->SetAttribute(ATT_FLOOD_COLOR, Color(0, 0, 0, 0));
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ ForSurface(DrawTarget* aDT, SourceSurface* aSurface,
+ const IntPoint& aSurfacePosition)
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
+ if (filter) {
+ filter->SetAttribute(ATT_TRANSFORM_MATRIX,
+ Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y));
+ filter->SetInput(IN_TRANSFORM_IN, aSurface);
+ return filter.forget();
+ }
+ return nullptr;
+ }
+
+ static already_AddRefed<FilterNode>
+ ToAlpha(DrawTarget* aDT, FilterNode* aInput)
+ {
+ float zero = 0.0f;
+ RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (transfer) {
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, &zero, 1);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, &zero, 1);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, &zero, 1);
+ transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
+ transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
+ return transfer.forget();
+ }
+ return nullptr;
+ }
+
+} // namespace FilterWrappers
+
+// A class that wraps a FilterNode and handles conversion between different
+// color models. Create FilterCachedColorModels with your original filter and
+// the color model that this filter outputs in natively, and then call
+// ->ForColorModel(colorModel) in order to get a FilterNode which outputs to
+// the specified colorModel.
+// Internally, this is achieved by wrapping the original FilterNode with
+// conversion FilterNodes. These filter nodes are cached in such a way that no
+// repeated or back-and-forth conversions happen.
+class FilterCachedColorModels
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels)
+ // aFilter can be null. In that case, ForColorModel will return a non-null
+ // completely transparent filter for all color models.
+ FilterCachedColorModels(DrawTarget* aDT,
+ FilterNode* aFilter,
+ ColorModel aOriginalColorModel);
+
+ // Get a FilterNode for the specified color model, guaranteed to be non-null.
+ already_AddRefed<FilterNode> ForColorModel(ColorModel aColorModel);
+
+ AlphaModel OriginalAlphaModel() const { return mOriginalColorModel.mAlphaModel; }
+
+private:
+ // Create the required FilterNode that will be cached by ForColorModel.
+ already_AddRefed<FilterNode> WrapForColorModel(ColorModel aColorModel);
+
+ RefPtr<DrawTarget> mDT;
+ ColorModel mOriginalColorModel;
+
+ // This array is indexed by ColorModel::ToIndex.
+ RefPtr<FilterNode> mFilterForColorModel[4];
+
+ ~FilterCachedColorModels() {}
+};
+
+FilterCachedColorModels::FilterCachedColorModels(DrawTarget* aDT,
+ FilterNode* aFilter,
+ ColorModel aOriginalColorModel)
+ : mDT(aDT)
+ , mOriginalColorModel(aOriginalColorModel)
+{
+ if (aFilter) {
+ mFilterForColorModel[aOriginalColorModel.ToIndex()] = aFilter;
+ } else {
+ RefPtr<FilterNode> clear = FilterWrappers::Clear(aDT);
+ mFilterForColorModel[0] = clear;
+ mFilterForColorModel[1] = clear;
+ mFilterForColorModel[2] = clear;
+ mFilterForColorModel[3] = clear;
+ }
+}
+
+already_AddRefed<FilterNode>
+FilterCachedColorModels::ForColorModel(ColorModel aColorModel)
+{
+ if (aColorModel == mOriginalColorModel) {
+ // Make sure to not call WrapForColorModel if our original filter node was
+ // null, because then we'd get an infinite recursion.
+ RefPtr<FilterNode> filter = mFilterForColorModel[mOriginalColorModel.ToIndex()];
+ return filter.forget();
+ }
+
+ if (!mFilterForColorModel[aColorModel.ToIndex()]) {
+ mFilterForColorModel[aColorModel.ToIndex()] = WrapForColorModel(aColorModel);
+ }
+ RefPtr<FilterNode> filter(mFilterForColorModel[aColorModel.ToIndex()]);
+ return filter.forget();
+}
+
+already_AddRefed<FilterNode>
+FilterCachedColorModels::WrapForColorModel(ColorModel aColorModel)
+{
+ // Convert one aspect at a time and recurse.
+ // Conversions between premultiplied / unpremultiplied color channels for the
+ // same color space can happen directly.
+ // Conversions between different color spaces can only happen on
+ // unpremultiplied color channels.
+
+ if (aColorModel.mAlphaModel == AlphaModel::Premultiplied) {
+ RefPtr<FilterNode> unpre =
+ ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Unpremultiplied));
+ return FilterWrappers::Premultiply(mDT, unpre);
+ }
+
+ MOZ_ASSERT(aColorModel.mAlphaModel == AlphaModel::Unpremultiplied);
+ if (aColorModel.mColorSpace == mOriginalColorModel.mColorSpace) {
+ RefPtr<FilterNode> premultiplied =
+ ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Premultiplied));
+ return FilterWrappers::Unpremultiply(mDT, premultiplied);
+ }
+
+ RefPtr<FilterNode> unpremultipliedOriginal =
+ ForColorModel(ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied));
+ if (aColorModel.mColorSpace == ColorSpace::LinearRGB) {
+ return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal);
+ }
+ return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal);
+}
+
+static const float identityMatrix[] =
+ { 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0 };
+
+// When aAmount == 0, the identity matrix is returned.
+// When aAmount == 1, aToMatrix is returned.
+// When aAmount > 1, an exaggerated version of aToMatrix is returned. This can
+// be useful in certain cases, such as producing a color matrix to oversaturate
+// an image.
+//
+// This function is a shortcut of a full matrix addition and a scalar multiply,
+// and it assumes that the following elements in aToMatrix are 0 and 1:
+// x x x 0 0
+// x x x 0 0
+// x x x 0 0
+// 0 0 0 1 0
+static void
+InterpolateFromIdentityMatrix(const float aToMatrix[20], float aAmount,
+ float aOutMatrix[20])
+{
+ PodCopy(aOutMatrix, identityMatrix, 20);
+
+ float oneMinusAmount = 1 - aAmount;
+
+ aOutMatrix[0] = aAmount * aToMatrix[0] + oneMinusAmount;
+ aOutMatrix[1] = aAmount * aToMatrix[1];
+ aOutMatrix[2] = aAmount * aToMatrix[2];
+
+ aOutMatrix[5] = aAmount * aToMatrix[5];
+ aOutMatrix[6] = aAmount * aToMatrix[6] + oneMinusAmount;
+ aOutMatrix[7] = aAmount * aToMatrix[7];
+
+ aOutMatrix[10] = aAmount * aToMatrix[10];
+ aOutMatrix[11] = aAmount * aToMatrix[11];
+ aOutMatrix[12] = aAmount * aToMatrix[12] + oneMinusAmount;
+}
+
+// Create a 4x5 color matrix for the different ways to specify color matrices
+// in SVG.
+static nsresult
+ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray<float>& aValues,
+ float aOutMatrix[20])
+{
+ // Luminance coefficients.
+ static const float lumR = 0.2126f;
+ static const float lumG = 0.7152f;
+ static const float lumB = 0.0722f;
+
+ static const float oneMinusLumR = 1 - lumR;
+ static const float oneMinusLumG = 1 - lumG;
+ static const float oneMinusLumB = 1 - lumB;
+
+ static const float luminanceToAlphaMatrix[] =
+ { 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ lumR, lumG, lumB, 0, 0 };
+
+ static const float saturateMatrix[] =
+ { lumR, lumG, lumB, 0, 0,
+ lumR, lumG, lumB, 0, 0,
+ lumR, lumG, lumB, 0, 0,
+ 0, 0, 0, 1, 0 };
+
+ static const float sepiaMatrix[] =
+ { 0.393f, 0.769f, 0.189f, 0, 0,
+ 0.349f, 0.686f, 0.168f, 0, 0,
+ 0.272f, 0.534f, 0.131f, 0, 0,
+ 0, 0, 0, 1, 0 };
+
+ // Hue rotate specific coefficients.
+ static const float hueRotateR = 0.143f;
+ static const float hueRotateG = 0.140f;
+ static const float hueRotateB = 0.283f;
+
+ switch (aColorMatrixType) {
+
+ case SVG_FECOLORMATRIX_TYPE_MATRIX:
+ {
+ if (aValues.Length() != 20) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PodCopy(aOutMatrix, aValues.Elements(), 20);
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_SATURATE:
+ {
+ if (aValues.Length() != 1)
+ return NS_ERROR_FAILURE;
+
+ float s = aValues[0];
+
+ if (s < 0)
+ return NS_ERROR_FAILURE;
+
+ InterpolateFromIdentityMatrix(saturateMatrix, 1 - s, aOutMatrix);
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE:
+ {
+ if (aValues.Length() != 1)
+ return NS_ERROR_FAILURE;
+
+ PodCopy(aOutMatrix, identityMatrix, 20);
+
+ float hueRotateValue = aValues[0];
+
+ float c = static_cast<float>(cos(hueRotateValue * M_PI / 180));
+ float s = static_cast<float>(sin(hueRotateValue * M_PI / 180));
+
+ aOutMatrix[0] = lumR + oneMinusLumR * c - lumR * s;
+ aOutMatrix[1] = lumG - lumG * c - lumG * s;
+ aOutMatrix[2] = lumB - lumB * c + oneMinusLumB * s;
+
+ aOutMatrix[5] = lumR - lumR * c + hueRotateR * s;
+ aOutMatrix[6] = lumG + oneMinusLumG * c + hueRotateG * s;
+ aOutMatrix[7] = lumB - lumB * c - hueRotateB * s;
+
+ aOutMatrix[10] = lumR - lumR * c - oneMinusLumR * s;
+ aOutMatrix[11] = lumG - lumG * c + lumG * s;
+ aOutMatrix[12] = lumB + oneMinusLumB * c + lumB * s;
+
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA:
+ {
+ PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20);
+ break;
+ }
+
+ case SVG_FECOLORMATRIX_TYPE_SEPIA:
+ {
+ if (aValues.Length() != 1)
+ return NS_ERROR_FAILURE;
+
+ float amount = aValues[0];
+
+ if (amount < 0 || amount > 1)
+ return NS_ERROR_FAILURE;
+
+ InterpolateFromIdentityMatrix(sepiaMatrix, amount, aOutMatrix);
+ break;
+ }
+
+ default:
+ return NS_ERROR_FAILURE;
+
+ }
+
+ return NS_OK;
+}
+
+static void
+DisableAllTransfers(FilterNode* aTransferFilterNode)
+{
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_R, true);
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_G, true);
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_B, true);
+ aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_A, true);
+}
+
+// Called for one channel at a time.
+// This function creates the required FilterNodes on demand and tries to
+// merge conversions of different channels into the same FilterNode if
+// possible.
+// There's a mismatch between the way SVG and the Moz2D API handle transfer
+// functions: In SVG, it's possible to specify a different transfer function
+// type for each color channel, but in Moz2D, a given transfer function type
+// applies to all color channels.
+//
+// @param aFunctionAttributes The attributes of the transfer function for this
+// channel.
+// @param aChannel The color channel that this function applies to, where
+// 0 = red, 1 = green, 2 = blue, 3 = alpha
+// @param aDT The DrawTarget that the FilterNodes should be created for.
+// @param aTableTransfer Existing FilterNode holders (which may still be
+// null) that the resulting FilterNodes from this
+// function will be stored in.
+//
+static void
+ConvertComponentTransferFunctionToFilter(const AttributeMap& aFunctionAttributes,
+ int32_t aChannel,
+ DrawTarget* aDT,
+ RefPtr<FilterNode>& aTableTransfer,
+ RefPtr<FilterNode>& aDiscreteTransfer,
+ RefPtr<FilterNode>& aLinearTransfer,
+ RefPtr<FilterNode>& aGammaTransfer)
+{
+ static const TransferAtts disableAtt[4] = {
+ ATT_TRANSFER_DISABLE_R,
+ ATT_TRANSFER_DISABLE_G,
+ ATT_TRANSFER_DISABLE_B,
+ ATT_TRANSFER_DISABLE_A
+ };
+
+ RefPtr<FilterNode> filter;
+
+ uint32_t type = aFunctionAttributes.GetUint(eComponentTransferFunctionType);
+
+ switch (type) {
+ case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 2)
+ return;
+
+ if (!aTableTransfer) {
+ aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER);
+ if (!aTableTransfer) {
+ return;
+ }
+ DisableAllTransfers(aTableTransfer);
+ }
+ filter = aTableTransfer;
+ static const TableTransferAtts tableAtt[4] = {
+ ATT_TABLE_TRANSFER_TABLE_R,
+ ATT_TABLE_TRANSFER_TABLE_G,
+ ATT_TABLE_TRANSFER_TABLE_B,
+ ATT_TABLE_TRANSFER_TABLE_A
+ };
+ filter->SetAttribute(disableAtt[aChannel], false);
+ filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 1)
+ return;
+
+ if (!aDiscreteTransfer) {
+ aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
+ if (!aDiscreteTransfer) {
+ return;
+ }
+ DisableAllTransfers(aDiscreteTransfer);
+ }
+ filter = aDiscreteTransfer;
+ static const DiscreteTransferAtts tableAtt[4] = {
+ ATT_DISCRETE_TRANSFER_TABLE_R,
+ ATT_DISCRETE_TRANSFER_TABLE_G,
+ ATT_DISCRETE_TRANSFER_TABLE_B,
+ ATT_DISCRETE_TRANSFER_TABLE_A
+ };
+ filter->SetAttribute(disableAtt[aChannel], false);
+ filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
+
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
+ {
+ static const LinearTransferAtts slopeAtt[4] = {
+ ATT_LINEAR_TRANSFER_SLOPE_R,
+ ATT_LINEAR_TRANSFER_SLOPE_G,
+ ATT_LINEAR_TRANSFER_SLOPE_B,
+ ATT_LINEAR_TRANSFER_SLOPE_A
+ };
+ static const LinearTransferAtts interceptAtt[4] = {
+ ATT_LINEAR_TRANSFER_INTERCEPT_R,
+ ATT_LINEAR_TRANSFER_INTERCEPT_G,
+ ATT_LINEAR_TRANSFER_INTERCEPT_B,
+ ATT_LINEAR_TRANSFER_INTERCEPT_A
+ };
+ if (!aLinearTransfer) {
+ aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER);
+ if (!aLinearTransfer) {
+ return;
+ }
+ DisableAllTransfers(aLinearTransfer);
+ }
+ filter = aLinearTransfer;
+ filter->SetAttribute(disableAtt[aChannel], false);
+ float slope = aFunctionAttributes.GetFloat(eComponentTransferFunctionSlope);
+ float intercept = aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
+ filter->SetAttribute(slopeAtt[aChannel], slope);
+ filter->SetAttribute(interceptAtt[aChannel], intercept);
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
+ {
+ static const GammaTransferAtts amplitudeAtt[4] = {
+ ATT_GAMMA_TRANSFER_AMPLITUDE_R,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_G,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_B,
+ ATT_GAMMA_TRANSFER_AMPLITUDE_A
+ };
+ static const GammaTransferAtts exponentAtt[4] = {
+ ATT_GAMMA_TRANSFER_EXPONENT_R,
+ ATT_GAMMA_TRANSFER_EXPONENT_G,
+ ATT_GAMMA_TRANSFER_EXPONENT_B,
+ ATT_GAMMA_TRANSFER_EXPONENT_A
+ };
+ static const GammaTransferAtts offsetAtt[4] = {
+ ATT_GAMMA_TRANSFER_OFFSET_R,
+ ATT_GAMMA_TRANSFER_OFFSET_G,
+ ATT_GAMMA_TRANSFER_OFFSET_B,
+ ATT_GAMMA_TRANSFER_OFFSET_A
+ };
+ if (!aGammaTransfer) {
+ aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER);
+ if (!aGammaTransfer) {
+ return;
+ }
+ DisableAllTransfers(aGammaTransfer);
+ }
+ filter = aGammaTransfer;
+ filter->SetAttribute(disableAtt[aChannel], false);
+ float amplitude = aFunctionAttributes.GetFloat(eComponentTransferFunctionAmplitude);
+ float exponent = aFunctionAttributes.GetFloat(eComponentTransferFunctionExponent);
+ float offset = aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
+ filter->SetAttribute(amplitudeAtt[aChannel], amplitude);
+ filter->SetAttribute(exponentAtt[aChannel], exponent);
+ filter->SetAttribute(offsetAtt[aChannel], offset);
+ break;
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
+ default:
+ break;
+ }
+}
+
+const int32_t kMorphologyMaxRadius = 100000;
+
+// Handle the different primitive description types and create the necessary
+// FilterNode(s) for each.
+// Returns nullptr for invalid filter primitives. This should be interpreted as
+// transparent black by the caller.
+// aSourceRegions contains the filter primitive subregions of the source
+// primitives; only needed for eTile primitives.
+// aInputImages carries additional surfaces that are used by eImage primitives.
+static already_AddRefed<FilterNode>
+FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescription,
+ DrawTarget* aDT,
+ nsTArray<RefPtr<FilterNode> >& aSources,
+ nsTArray<IntRect>& aSourceRegions,
+ nsTArray<RefPtr<SourceSurface>>& aInputImages)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Empty:
+ return nullptr;
+
+ case PrimitiveType::Blend:
+ {
+ uint32_t mode = atts.GetUint(eBlendBlendmode);
+ RefPtr<FilterNode> filter;
+ if (mode == SVG_FEBLEND_MODE_UNKNOWN) {
+ return nullptr;
+ }
+ if (mode == SVG_FEBLEND_MODE_NORMAL) {
+ filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
+ filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+ } else {
+ filter = aDT->CreateFilter(FilterType::BLEND);
+ if (!filter) {
+ return nullptr;
+ }
+ static const uint8_t blendModes[SVG_FEBLEND_MODE_LUMINOSITY + 1] = {
+ 0,
+ 0,
+ BLEND_MODE_MULTIPLY,
+ BLEND_MODE_SCREEN,
+ BLEND_MODE_DARKEN,
+ BLEND_MODE_LIGHTEN,
+ BLEND_MODE_OVERLAY,
+ BLEND_MODE_COLOR_DODGE,
+ BLEND_MODE_COLOR_BURN,
+ BLEND_MODE_HARD_LIGHT,
+ BLEND_MODE_SOFT_LIGHT,
+ BLEND_MODE_DIFFERENCE,
+ BLEND_MODE_EXCLUSION,
+ BLEND_MODE_HUE,
+ BLEND_MODE_SATURATION,
+ BLEND_MODE_COLOR,
+ BLEND_MODE_LUMINOSITY
+ };
+ filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
+ // The correct input order for both software and D2D filters is flipped
+ // from our source order, so flip here.
+ filter->SetInput(IN_BLEND_IN, aSources[1]);
+ filter->SetInput(IN_BLEND_IN2, aSources[0]);
+ }
+ return filter.forget();
+ }
+
+ case PrimitiveType::ColorMatrix:
+ {
+ float colorMatrix[20];
+ uint32_t type = atts.GetUint(eColorMatrixType);
+ const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
+ if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix)) ||
+ PodEqual(colorMatrix, identityMatrix)) {
+ RefPtr<FilterNode> filter(aSources[0]);
+ return filter.forget();
+ }
+ Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10], colorMatrix[15],
+ colorMatrix[1], colorMatrix[6], colorMatrix[11], colorMatrix[16],
+ colorMatrix[2], colorMatrix[7], colorMatrix[12], colorMatrix[17],
+ colorMatrix[3], colorMatrix[8], colorMatrix[13], colorMatrix[18],
+ colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]);
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COLOR_MATRIX);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix);
+ filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE, (uint32_t)ALPHA_MODE_STRAIGHT);
+ filter->SetInput(IN_COLOR_MATRIX_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Morphology:
+ {
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = radii.width;
+ int32_t ry = radii.height;
+ if (rx < 0 || ry < 0) {
+ // XXX SVGContentUtils::ReportToConsole()
+ return nullptr;
+ }
+ if (rx == 0 && ry == 0) {
+ return nullptr;
+ }
+
+ // Clamp radii to prevent completely insane values:
+ rx = std::min(rx, kMorphologyMaxRadius);
+ ry = std::min(ry, kMorphologyMaxRadius);
+
+ MorphologyOperator op = atts.GetUint(eMorphologyOperator) == SVG_OPERATOR_ERODE ?
+ MORPHOLOGY_OPERATOR_ERODE : MORPHOLOGY_OPERATOR_DILATE;
+
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::MORPHOLOGY);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry));
+ filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op);
+ filter->SetInput(IN_MORPHOLOGY_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Flood:
+ {
+ Color color = atts.GetColor(eFloodColor);
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_FLOOD_COLOR, color);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Tile:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TILE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_TILE_SOURCE_RECT, aSourceRegions[0]);
+ filter->SetInput(IN_TILE_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::ComponentTransfer:
+ {
+ RefPtr<FilterNode> filters[4]; // one for each FILTER_*_TRANSFER type
+ static const AttributeName componentFunctionNames[4] = {
+ eComponentTransferFunctionR,
+ eComponentTransferFunctionG,
+ eComponentTransferFunctionB,
+ eComponentTransferFunctionA
+ };
+ for (int32_t i = 0; i < 4; i++) {
+ AttributeMap functionAttributes =
+ atts.GetAttributeMap(componentFunctionNames[i]);
+ ConvertComponentTransferFunctionToFilter(functionAttributes, i, aDT,
+ filters[0], filters[1], filters[2], filters[3]);
+ }
+
+ // Connect all used filters nodes.
+ RefPtr<FilterNode> lastFilter = aSources[0];
+ for (int32_t i = 0; i < 4; i++) {
+ if (filters[i]) {
+ filters[i]->SetInput(0, lastFilter);
+ lastFilter = filters[i];
+ }
+ }
+
+ return lastFilter.forget();
+ }
+
+ case PrimitiveType::ConvolveMatrix:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, atts.GetIntSize(eConvolveMatrixKernelSize));
+ const nsTArray<float>& matrix = atts.GetFloats(eConvolveMatrixKernelMatrix);
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX,
+ matrix.Elements(), matrix.Length());
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR,
+ atts.GetFloat(eConvolveMatrixDivisor));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS,
+ atts.GetFloat(eConvolveMatrixBias));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET,
+ atts.GetIntPoint(eConvolveMatrixTarget));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT,
+ aSourceRegions[0]);
+ uint32_t edgeMode = atts.GetUint(eConvolveMatrixEdgeMode);
+ static const uint8_t edgeModes[SVG_EDGEMODE_NONE+1] = {
+ EDGE_MODE_NONE, // SVG_EDGEMODE_UNKNOWN
+ EDGE_MODE_DUPLICATE, // SVG_EDGEMODE_DUPLICATE
+ EDGE_MODE_WRAP, // SVG_EDGEMODE_WRAP
+ EDGE_MODE_NONE // SVG_EDGEMODE_NONE
+ };
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE, (uint32_t)edgeModes[edgeMode]);
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
+ atts.GetSize(eConvolveMatrixKernelUnitLength));
+ filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA,
+ atts.GetBool(eConvolveMatrixPreserveAlpha));
+ filter->SetInput(IN_CONVOLVE_MATRIX_IN, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Offset:
+ {
+ return FilterWrappers::Offset(aDT, aSources[0],
+ atts.GetIntPoint(eOffsetOffset));
+ }
+
+ case PrimitiveType::DisplacementMap:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE,
+ atts.GetFloat(eDisplacementMapScale));
+ static const uint8_t channel[SVG_CHANNEL_A+1] = {
+ COLOR_CHANNEL_R, // SVG_CHANNEL_UNKNOWN
+ COLOR_CHANNEL_R, // SVG_CHANNEL_R
+ COLOR_CHANNEL_G, // SVG_CHANNEL_G
+ COLOR_CHANNEL_B, // SVG_CHANNEL_B
+ COLOR_CHANNEL_A // SVG_CHANNEL_A
+ };
+ filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL,
+ (uint32_t)channel[atts.GetUint(eDisplacementMapXChannel)]);
+ filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL,
+ (uint32_t)channel[atts.GetUint(eDisplacementMapYChannel)]);
+ filter->SetInput(IN_DISPLACEMENT_MAP_IN, aSources[0]);
+ filter->SetInput(IN_DISPLACEMENT_MAP_IN2, aSources[1]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::Turbulence:
+ {
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TURBULENCE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY,
+ atts.GetSize(eTurbulenceBaseFrequency));
+ filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES,
+ atts.GetUint(eTurbulenceNumOctaves));
+ filter->SetAttribute(ATT_TURBULENCE_STITCHABLE,
+ atts.GetBool(eTurbulenceStitchable));
+ filter->SetAttribute(ATT_TURBULENCE_SEED,
+ (uint32_t)atts.GetFloat(eTurbulenceSeed));
+ static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE+1] = {
+ TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_UNKNOWN
+ TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_FRACTALNOISE
+ TURBULENCE_TYPE_TURBULENCE // SVG_TURBULENCE_TYPE_TURBULENCE
+ };
+ filter->SetAttribute(ATT_TURBULENCE_TYPE,
+ (uint32_t)type[atts.GetUint(eTurbulenceType)]);
+ filter->SetAttribute(ATT_TURBULENCE_RECT,
+ aDescription.PrimitiveSubregion() - atts.GetIntPoint(eTurbulenceOffset));
+ return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset));
+ }
+
+ case PrimitiveType::Composite:
+ {
+ RefPtr<FilterNode> filter;
+ uint32_t op = atts.GetUint(eCompositeOperator);
+ if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
+ const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+ static const float allZero[4] = { 0, 0, 0, 0 };
+ filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
+ // All-zero coefficients sometimes occur in junk filters.
+ if (!filter ||
+ (coefficients.Length() == ArrayLength(allZero) &&
+ PodEqual(coefficients.Elements(), allZero, ArrayLength(allZero)))) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
+ coefficients.Elements(), coefficients.Length());
+ filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]);
+ filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]);
+ } else {
+ filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_ARITHMETIC] = {
+ COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_UNKNOWN
+ COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_OVER
+ COMPOSITE_OPERATOR_IN, // SVG_FECOMPOSITE_OPERATOR_IN
+ COMPOSITE_OPERATOR_OUT, // SVG_FECOMPOSITE_OPERATOR_OUT
+ COMPOSITE_OPERATOR_ATOP, // SVG_FECOMPOSITE_OPERATOR_ATOP
+ COMPOSITE_OPERATOR_XOR // SVG_FECOMPOSITE_OPERATOR_XOR
+ };
+ filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]);
+ filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
+ filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+ }
+ return filter.forget();
+ }
+
+ case PrimitiveType::Merge:
+ {
+ if (aSources.Length() == 0) {
+ return nullptr;
+ }
+ if (aSources.Length() == 1) {
+ RefPtr<FilterNode> filter(aSources[0]);
+ return filter.forget();
+ }
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
+ for (size_t i = 0; i < aSources.Length(); i++) {
+ filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]);
+ }
+ return filter.forget();
+ }
+
+ case PrimitiveType::GaussianBlur:
+ {
+ return FilterWrappers::GaussianBlur(aDT, aSources[0],
+ atts.GetSize(eGaussianBlurStdDeviation));
+ }
+
+ case PrimitiveType::DropShadow:
+ {
+ RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(aDT, aSources[0]);
+ RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(aDT, alpha,
+ atts.GetSize(eDropShadowStdDeviation));
+ RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(aDT, blur,
+ atts.GetIntPoint(eDropShadowOffset));
+ RefPtr<FilterNode> flood = aDT->CreateFilter(FilterType::FLOOD);
+ if (!flood) {
+ return nullptr;
+ }
+ Color color = atts.GetColor(eDropShadowColor);
+ if (aDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
+ color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
+ gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
+ gsRGBToLinearRGBMap[uint8_t(color.b * 255)],
+ color.a);
+ }
+ flood->SetAttribute(ATT_FLOOD_COLOR, color);
+
+ RefPtr<FilterNode> composite = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!composite) {
+ return nullptr;
+ }
+ composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN);
+ composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
+ composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
+
+ RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
+ if (!filter) {
+ return nullptr;
+ }
+ filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
+ filter->SetInput(IN_COMPOSITE_IN_START, composite);
+ filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
+ return filter.forget();
+ }
+
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ bool isSpecular =
+ aDescription.Type() == PrimitiveType::SpecularLighting;
+
+ AttributeMap lightAttributes = atts.GetAttributeMap(eLightingLight);
+
+ if (lightAttributes.GetUint(eLightType) == eLightTypeNone) {
+ return nullptr;
+ }
+
+ enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
+
+ switch (lightAttributes.GetUint(eLightType)) {
+ case eLightTypePoint: lightType = POINT; break;
+ case eLightTypeSpot: lightType = SPOT; break;
+ case eLightTypeDistant: lightType = DISTANT; break;
+ }
+
+ static const FilterType filterType[2][DISTANT+1] = {
+ { FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE, FilterType::DISTANT_DIFFUSE },
+ { FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR, FilterType::DISTANT_SPECULAR }
+ };
+ RefPtr<FilterNode> filter =
+ aDT->CreateFilter(filterType[isSpecular][lightType]);
+ if (!filter) {
+ return nullptr;
+ }
+
+ filter->SetAttribute(ATT_LIGHTING_COLOR,
+ atts.GetColor(eLightingColor));
+ filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE,
+ atts.GetFloat(eLightingSurfaceScale));
+ filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH,
+ atts.GetSize(eLightingKernelUnitLength));
+
+ if (isSpecular) {
+ filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
+ atts.GetFloat(eSpecularLightingSpecularConstant));
+ filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT,
+ atts.GetFloat(eSpecularLightingSpecularExponent));
+ } else {
+ filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT,
+ atts.GetFloat(eDiffuseLightingDiffuseConstant));
+ }
+
+ switch (lightType) {
+ case POINT:
+ filter->SetAttribute(ATT_POINT_LIGHT_POSITION,
+ lightAttributes.GetPoint3D(ePointLightPosition));
+ break;
+ case SPOT:
+ filter->SetAttribute(ATT_SPOT_LIGHT_POSITION,
+ lightAttributes.GetPoint3D(eSpotLightPosition));
+ filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT,
+ lightAttributes.GetPoint3D(eSpotLightPointsAt));
+ filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS,
+ lightAttributes.GetFloat(eSpotLightFocus));
+ filter->SetAttribute(ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
+ lightAttributes.GetFloat(eSpotLightLimitingConeAngle));
+ break;
+ case DISTANT:
+ filter->SetAttribute(ATT_DISTANT_LIGHT_AZIMUTH,
+ lightAttributes.GetFloat(eDistantLightAzimuth));
+ filter->SetAttribute(ATT_DISTANT_LIGHT_ELEVATION,
+ lightAttributes.GetFloat(eDistantLightElevation));
+ break;
+ }
+
+ filter->SetInput(IN_LIGHTING_IN, aSources[0]);
+
+ return filter.forget();
+ }
+
+ case PrimitiveType::Image:
+ {
+ Matrix TM = atts.GetMatrix(eImageTransform);
+ if (!TM.Determinant()) {
+ return nullptr;
+ }
+
+ // Pull the image from the additional image list using the index that's
+ // stored in the primitive description.
+ RefPtr<SourceSurface> inputImage =
+ aInputImages[atts.GetUint(eImageInputIndex)];
+
+ RefPtr<FilterNode> transform = aDT->CreateFilter(FilterType::TRANSFORM);
+ if (!transform) {
+ return nullptr;
+ }
+ transform->SetInput(IN_TRANSFORM_IN, inputImage);
+ transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
+ transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter));
+ return transform.forget();
+ }
+
+ case PrimitiveType::ToAlpha:
+ {
+ return FilterWrappers::ToAlpha(aDT, aSources[0]);
+ }
+
+ default:
+ return nullptr;
+ }
+}
+
+template<typename T>
+static const T&
+ElementForIndex(int32_t aIndex,
+ const nsTArray<T>& aPrimitiveElements,
+ const T& aSourceGraphicElement,
+ const T& aFillPaintElement,
+ const T& aStrokePaintElement)
+{
+ switch (aIndex) {
+ case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic:
+ case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha:
+ return aSourceGraphicElement;
+ case FilterPrimitiveDescription::kPrimitiveIndexFillPaint:
+ return aFillPaintElement;
+ case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint:
+ return aStrokePaintElement;
+ default:
+ MOZ_ASSERT(aIndex >= 0, "bad index");
+ return aPrimitiveElements[aIndex];
+ }
+}
+
+static AlphaModel
+InputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
+ int32_t aInputIndex,
+ AlphaModel aOriginalAlphaModel)
+{
+ switch (aDescr.Type()) {
+ case PrimitiveType::Tile:
+ case PrimitiveType::Offset:
+ case PrimitiveType::ToAlpha:
+ return aOriginalAlphaModel;
+
+ case PrimitiveType::ColorMatrix:
+ case PrimitiveType::ComponentTransfer:
+ return AlphaModel::Unpremultiplied;
+
+ case PrimitiveType::DisplacementMap:
+ return aInputIndex == 0 ?
+ AlphaModel::Premultiplied : AlphaModel::Unpremultiplied;
+
+ case PrimitiveType::ConvolveMatrix:
+ return aDescr.Attributes().GetBool(eConvolveMatrixPreserveAlpha) ?
+ AlphaModel::Unpremultiplied : AlphaModel::Premultiplied;
+
+ default:
+ return AlphaModel::Premultiplied;
+ }
+}
+
+static AlphaModel
+OutputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
+ const nsTArray<AlphaModel>& aInputAlphaModels)
+{
+ if (aInputAlphaModels.Length()) {
+ // For filters with inputs, the output is premultiplied if and only if the
+ // first input is premultiplied.
+ return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]);
+ }
+
+ // All filters without inputs produce premultiplied alpha.
+ return AlphaModel::Premultiplied;
+}
+
+// Returns the output FilterNode, in premultiplied sRGB space.
+static already_AddRefed<FilterNode>
+FilterNodeGraphFromDescription(DrawTarget* aDT,
+ const FilterDescription& aFilter,
+ const Rect& aResultNeededRect,
+ SourceSurface* aSourceGraphic,
+ const IntRect& aSourceGraphicRect,
+ SourceSurface* aFillPaint,
+ const IntRect& aFillPaintRect,
+ SourceSurface* aStrokePaint,
+ const IntRect& aStrokePaintRect,
+ nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
+
+ RefPtr<FilterCachedColorModels> sourceFilters[4];
+ nsTArray<RefPtr<FilterCachedColorModels> > primitiveFilters;
+
+ for (size_t i = 0; i < primitives.Length(); ++i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+
+ nsTArray<RefPtr<FilterNode> > inputFilterNodes;
+ nsTArray<IntRect> inputSourceRects;
+ nsTArray<AlphaModel> inputAlphaModels;
+
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ if (inputIndex < 0) {
+ inputSourceRects.AppendElement(descr.FilterSpaceBounds());
+ } else {
+ inputSourceRects.AppendElement(primitives[inputIndex].PrimitiveSubregion());
+ }
+
+ RefPtr<FilterCachedColorModels> inputFilter;
+ if (inputIndex >= 0) {
+ MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(), "out-of-bounds input index!");
+ inputFilter = primitiveFilters[inputIndex];
+ MOZ_ASSERT(inputFilter, "Referred to input filter that comes after the current one?");
+ } else {
+ int32_t sourceIndex = -inputIndex - 1;
+ MOZ_ASSERT(sourceIndex >= 0, "invalid source index");
+ MOZ_ASSERT(sourceIndex < 4, "invalid source index");
+ inputFilter = sourceFilters[sourceIndex];
+ if (!inputFilter) {
+ RefPtr<FilterNode> sourceFilterNode;
+
+ nsTArray<SourceSurface*> primitiveSurfaces;
+ nsTArray<IntRect> primitiveSurfaceRects;
+ RefPtr<SourceSurface> surf =
+ ElementForIndex(inputIndex, primitiveSurfaces,
+ aSourceGraphic, aFillPaint, aStrokePaint);
+ IntRect surfaceRect =
+ ElementForIndex(inputIndex, primitiveSurfaceRects,
+ aSourceGraphicRect, aFillPaintRect, aStrokePaintRect);
+ if (surf) {
+ IntPoint offset = surfaceRect.TopLeft();
+ sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset);
+
+ // Clip the original SourceGraphic to the first filter region if the
+ // surface isn't already sized appropriately.
+ if ((inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic ||
+ inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) &&
+ !descr.FilterSpaceBounds().Contains(aSourceGraphicRect)) {
+ sourceFilterNode =
+ FilterWrappers::Crop(aDT, sourceFilterNode, descr.FilterSpaceBounds());
+ }
+
+ if (inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) {
+ sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode);
+ }
+ }
+
+ inputFilter = new FilterCachedColorModels(aDT, sourceFilterNode,
+ ColorModel::PremulSRGB());
+ sourceFilters[sourceIndex] = inputFilter;
+ }
+ }
+ MOZ_ASSERT(inputFilter);
+
+ AlphaModel inputAlphaModel =
+ InputAlphaModelForPrimitive(descr, j, inputFilter->OriginalAlphaModel());
+ inputAlphaModels.AppendElement(inputAlphaModel);
+ ColorModel inputColorModel(descr.InputColorSpace(j), inputAlphaModel);
+ inputFilterNodes.AppendElement(inputFilter->ForColorModel(inputColorModel));
+ }
+
+ RefPtr<FilterNode> primitiveFilterNode =
+ FilterNodeFromPrimitiveDescription(descr, aDT, inputFilterNodes,
+ inputSourceRects, aAdditionalImages);
+
+ if (primitiveFilterNode) {
+ primitiveFilterNode =
+ FilterWrappers::Crop(aDT, primitiveFilterNode, descr.PrimitiveSubregion());
+ }
+
+ ColorModel outputColorModel(descr.OutputColorSpace(),
+ OutputAlphaModelForPrimitive(descr, inputAlphaModels));
+ RefPtr<FilterCachedColorModels> primitiveFilter =
+ new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel);
+
+ primitiveFilters.AppendElement(primitiveFilter);
+ }
+
+ MOZ_RELEASE_ASSERT(!primitiveFilters.IsEmpty());
+ return primitiveFilters.LastElement()->ForColorModel(ColorModel::PremulSRGB());
+}
+
+// FilterSupport
+
+void
+FilterSupport::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)
+{
+ RefPtr<FilterNode> resultFilter =
+ FilterNodeGraphFromDescription(aDT, aFilter, aRenderRect,
+ aSourceGraphic, aSourceGraphicRect, aFillPaint, aFillPaintRect,
+ aStrokePaint, aStrokePaintRect, aAdditionalImages);
+ if (!resultFilter) {
+ gfxWarning() << "Filter is NULL.";
+ return;
+ }
+ aDT->DrawFilter(resultFilter, aRenderRect, aDestPoint, aOptions);
+}
+
+static nsIntRegion
+UnionOfRegions(const nsTArray<nsIntRegion>& aRegions)
+{
+ nsIntRegion result;
+ for (size_t i = 0; i < aRegions.Length(); i++) {
+ result.Or(result, aRegions[i]);
+ }
+ return result;
+}
+
+static int32_t
+InflateSizeForBlurStdDev(float aStdDev)
+{
+ double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
+ return uint32_t(floor(size + 0.5));
+}
+
+static nsIntRegion
+ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsTArray<nsIntRegion>& aInputChangeRegions)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Empty:
+ case PrimitiveType::Flood:
+ case PrimitiveType::Turbulence:
+ case PrimitiveType::Image:
+ return nsIntRegion();
+
+ case PrimitiveType::Blend:
+ case PrimitiveType::Composite:
+ case PrimitiveType::Merge:
+ return UnionOfRegions(aInputChangeRegions);
+
+ case PrimitiveType::ColorMatrix:
+ case PrimitiveType::ComponentTransfer:
+ case PrimitiveType::ToAlpha:
+ return aInputChangeRegions[0];
+
+ case PrimitiveType::Morphology:
+ {
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+ int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+ return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+ }
+
+ case PrimitiveType::Tile:
+ return aDescription.PrimitiveSubregion();
+
+ case PrimitiveType::ConvolveMatrix:
+ {
+ if (atts.GetUint(eConvolveMatrixEdgeMode) != EDGE_MODE_NONE) {
+ return aDescription.PrimitiveSubregion();
+ }
+ Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
+ IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
+ IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
+ nsIntMargin m(ceil(kernelUnitLength.width * (target.x)),
+ ceil(kernelUnitLength.height * (target.y)),
+ ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
+ ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)));
+ return aInputChangeRegions[0].Inflated(m);
+ }
+
+ case PrimitiveType::Offset:
+ {
+ IntPoint offset = atts.GetIntPoint(eOffsetOffset);
+ return aInputChangeRegions[0].MovedBy(offset.x, offset.y);
+ }
+
+ case PrimitiveType::DisplacementMap:
+ {
+ int32_t scale = ceil(std::abs(atts.GetFloat(eDisplacementMapScale)));
+ return aInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale));
+ }
+
+ case PrimitiveType::GaussianBlur:
+ {
+ Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ case PrimitiveType::DropShadow:
+ {
+ IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+ nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y);
+ Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ blurRegion.Or(blurRegion, aInputChangeRegions[0]);
+ return blurRegion;
+ }
+
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
+ int32_t dx = ceil(kernelUnitLength.width);
+ int32_t dy = ceil(kernelUnitLength.height);
+ return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ default:
+ return nsIntRegion();
+ }
+}
+
+/* static */ nsIntRegion
+FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter,
+ const nsIntRegion& aSourceGraphicChange,
+ const nsIntRegion& aFillPaintChange,
+ const nsIntRegion& aStrokePaintChange)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
+
+ nsTArray<nsIntRegion> resultChangeRegions;
+
+ for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+
+ nsTArray<nsIntRegion> inputChangeRegions;
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ MOZ_ASSERT(inputIndex < i, "bad input index");
+ nsIntRegion inputChangeRegion =
+ ElementForIndex(inputIndex, resultChangeRegions,
+ aSourceGraphicChange, aFillPaintChange,
+ aStrokePaintChange);
+ inputChangeRegions.AppendElement(inputChangeRegion);
+ }
+ nsIntRegion changeRegion =
+ ResultChangeRegionForPrimitive(descr, inputChangeRegions);
+ changeRegion.And(changeRegion, descr.PrimitiveSubregion());
+ resultChangeRegions.AppendElement(changeRegion);
+ }
+
+ MOZ_RELEASE_ASSERT(!resultChangeRegions.IsEmpty());
+ return resultChangeRegions[resultChangeRegions.Length() - 1];
+}
+
+static float
+ResultOfZeroUnderTransferFunction(const AttributeMap& aFunctionAttributes)
+{
+ switch (aFunctionAttributes.GetUint(eComponentTransferFunctionType)) {
+ case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 2) {
+ return 0.0f;
+ }
+ return tableValues[0];
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
+ {
+ const nsTArray<float>& tableValues =
+ aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
+ if (tableValues.Length() < 1) {
+ return 0.0f;
+ }
+ return tableValues[0];
+ }
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
+ return aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
+ return aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
+
+ case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
+ default:
+ return 0.0f;
+ }
+}
+
+nsIntRegion
+FilterSupport::PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsTArray<nsIntRegion>& aInputExtents)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Empty:
+ return IntRect();
+
+ case PrimitiveType::Composite:
+ {
+ uint32_t op = atts.GetUint(eCompositeOperator);
+ if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
+ // The arithmetic composite primitive can draw outside the bounding
+ // box of its source images.
+ const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+ MOZ_ASSERT(coefficients.Length() == 4);
+
+ // The calculation is:
+ // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
+ nsIntRegion region;
+ if (coefficients[0] > 0.0f) {
+ region = aInputExtents[0].Intersect(aInputExtents[1]);
+ }
+ if (coefficients[1] > 0.0f) {
+ region.Or(region, aInputExtents[0]);
+ }
+ if (coefficients[2] > 0.0f) {
+ region.Or(region, aInputExtents[1]);
+ }
+ if (coefficients[3] > 0.0f) {
+ region = aDescription.PrimitiveSubregion();
+ }
+ return region;
+ }
+ if (op == SVG_FECOMPOSITE_OPERATOR_IN) {
+ return aInputExtents[0].Intersect(aInputExtents[1]);
+ }
+ return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
+ }
+
+ case PrimitiveType::Flood:
+ {
+ if (atts.GetColor(eFloodColor).a == 0.0f) {
+ return IntRect();
+ }
+ return aDescription.PrimitiveSubregion();
+ }
+
+ case PrimitiveType::ColorMatrix:
+ {
+ if (atts.GetUint(eColorMatrixType) == (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX) {
+ const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
+ if (values.Length() == 20 && values[19] > 0.0f) {
+ return aDescription.PrimitiveSubregion();
+ }
+ }
+ return aInputExtents[0];
+ }
+
+ case PrimitiveType::ComponentTransfer:
+ {
+ AttributeMap functionAttributes =
+ atts.GetAttributeMap(eComponentTransferFunctionA);
+ if (ResultOfZeroUnderTransferFunction(functionAttributes) > 0.0f) {
+ return aDescription.PrimitiveSubregion();
+ }
+ return aInputExtents[0];
+ }
+
+ case PrimitiveType::Turbulence:
+ case PrimitiveType::Image:
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ return aDescription.PrimitiveSubregion();
+ }
+
+ case PrimitiveType::Morphology:
+ {
+ uint32_t op = atts.GetUint(eMorphologyOperator);
+ if (op == SVG_OPERATOR_ERODE) {
+ return aInputExtents[0];
+ }
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+ int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+ return aInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
+ }
+
+ default:
+ return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
+ }
+}
+
+/* static */ nsIntRegion
+FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter,
+ const nsIntRegion& aSourceGraphicExtents)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
+ nsTArray<nsIntRegion> postFilterExtents;
+
+ for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+ nsIntRegion filterSpace = descr.FilterSpaceBounds();
+
+ nsTArray<nsIntRegion> inputExtents;
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ MOZ_ASSERT(inputIndex < i, "bad input index");
+ nsIntRegion inputExtent =
+ ElementForIndex(inputIndex, postFilterExtents,
+ aSourceGraphicExtents, filterSpace, filterSpace);
+ inputExtents.AppendElement(inputExtent);
+ }
+ nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents);
+ extent.And(extent, descr.PrimitiveSubregion());
+ postFilterExtents.AppendElement(extent);
+ }
+
+ MOZ_RELEASE_ASSERT(!postFilterExtents.IsEmpty());
+ return postFilterExtents[postFilterExtents.Length() - 1];
+}
+
+static nsIntRegion
+SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
+ const nsIntRegion& aResultNeededRegion,
+ int32_t aInputIndex)
+{
+ const AttributeMap& atts = aDescription.Attributes();
+ switch (aDescription.Type()) {
+
+ case PrimitiveType::Flood:
+ case PrimitiveType::Turbulence:
+ case PrimitiveType::Image:
+ MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
+ return nsIntRegion();
+
+ case PrimitiveType::Empty:
+ return nsIntRegion();
+
+ case PrimitiveType::Blend:
+ case PrimitiveType::Composite:
+ case PrimitiveType::Merge:
+ case PrimitiveType::ColorMatrix:
+ case PrimitiveType::ComponentTransfer:
+ case PrimitiveType::ToAlpha:
+ return aResultNeededRegion;
+
+ case PrimitiveType::Morphology:
+ {
+ Size radii = atts.GetSize(eMorphologyRadii);
+ int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
+ int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
+ return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
+ }
+
+ case PrimitiveType::Tile:
+ return IntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX);
+
+ case PrimitiveType::ConvolveMatrix:
+ {
+ Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
+ IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
+ IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
+ nsIntMargin m(ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
+ ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)),
+ ceil(kernelUnitLength.width * (target.x)),
+ ceil(kernelUnitLength.height * (target.y)));
+ return aResultNeededRegion.Inflated(m);
+ }
+
+ case PrimitiveType::Offset:
+ {
+ IntPoint offset = atts.GetIntPoint(eOffsetOffset);
+ return aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+ }
+
+ case PrimitiveType::DisplacementMap:
+ {
+ if (aInputIndex == 1) {
+ return aResultNeededRegion;
+ }
+ int32_t scale = ceil(std::abs(atts.GetFloat(eDisplacementMapScale)));
+ return aResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale));
+ }
+
+ case PrimitiveType::GaussianBlur:
+ {
+ Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ case PrimitiveType::DropShadow:
+ {
+ IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
+ nsIntRegion offsetRegion =
+ aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
+ Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
+ int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
+ int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
+ nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ blurRegion.Or(blurRegion, aResultNeededRegion);
+ return blurRegion;
+ }
+
+ case PrimitiveType::DiffuseLighting:
+ case PrimitiveType::SpecularLighting:
+ {
+ Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
+ int32_t dx = ceil(kernelUnitLength.width);
+ int32_t dy = ceil(kernelUnitLength.height);
+ return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
+ }
+
+ default:
+ return nsIntRegion();
+ }
+
+}
+
+/* static */ void
+FilterSupport::ComputeSourceNeededRegions(const FilterDescription& aFilter,
+ const nsIntRegion& aResultNeededRegion,
+ nsIntRegion& aSourceGraphicNeededRegion,
+ nsIntRegion& aFillPaintNeededRegion,
+ nsIntRegion& aStrokePaintNeededRegion)
+{
+ const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
+ MOZ_ASSERT(!primitives.IsEmpty());
+ if (primitives.IsEmpty()) {
+ return;
+ }
+
+ nsTArray<nsIntRegion> primitiveNeededRegions;
+ primitiveNeededRegions.AppendElements(primitives.Length());
+
+ primitiveNeededRegions[primitives.Length() - 1] = aResultNeededRegion;
+
+ for (int32_t i = primitives.Length() - 1; i >= 0; --i) {
+ const FilterPrimitiveDescription& descr = primitives[i];
+ nsIntRegion neededRegion = primitiveNeededRegions[i];
+ neededRegion.And(neededRegion, descr.PrimitiveSubregion());
+
+ for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
+ int32_t inputIndex = descr.InputPrimitiveIndex(j);
+ MOZ_ASSERT(inputIndex < i, "bad input index");
+ nsIntRegion* inputNeededRegion = const_cast<nsIntRegion*>(
+ &ElementForIndex(inputIndex, primitiveNeededRegions,
+ aSourceGraphicNeededRegion,
+ aFillPaintNeededRegion, aStrokePaintNeededRegion));
+ inputNeededRegion->Or(*inputNeededRegion,
+ SourceNeededRegionForPrimitive(descr, neededRegion, j));
+ }
+ }
+
+ // Clip original SourceGraphic to first filter region.
+ const FilterPrimitiveDescription& firstDescr = primitives[0];
+ aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
+ firstDescr.FilterSpaceBounds());
+}
+
+// FilterPrimitiveDescription
+
+FilterPrimitiveDescription::FilterPrimitiveDescription()
+ : mType(PrimitiveType::Empty)
+ , mOutputColorSpace(ColorSpace::SRGB)
+ , mIsTainted(false)
+{
+}
+
+FilterPrimitiveDescription::FilterPrimitiveDescription(PrimitiveType aType)
+ : mType(aType)
+ , mOutputColorSpace(ColorSpace::SRGB)
+ , mIsTainted(false)
+{
+}
+
+FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther)
+ : mType(aOther.mType)
+ , mAttributes(aOther.mAttributes)
+ , mInputPrimitives(aOther.mInputPrimitives)
+ , mFilterPrimitiveSubregion(aOther.mFilterPrimitiveSubregion)
+ , mFilterSpaceBounds(aOther.mFilterSpaceBounds)
+ , mInputColorSpaces(aOther.mInputColorSpaces)
+ , mOutputColorSpace(aOther.mOutputColorSpace)
+ , mIsTainted(aOther.mIsTainted)
+{
+}
+
+FilterPrimitiveDescription&
+FilterPrimitiveDescription::operator=(const FilterPrimitiveDescription& aOther)
+{
+ if (this != &aOther) {
+ mType = aOther.mType;
+ mAttributes = aOther.mAttributes;
+ mInputPrimitives = aOther.mInputPrimitives;
+ mFilterPrimitiveSubregion = aOther.mFilterPrimitiveSubregion;
+ mFilterSpaceBounds = aOther.mFilterSpaceBounds;
+ mInputColorSpaces = aOther.mInputColorSpaces;
+ mOutputColorSpace = aOther.mOutputColorSpace;
+ mIsTainted = aOther.mIsTainted;
+ }
+ return *this;
+}
+
+bool
+FilterPrimitiveDescription::operator==(const FilterPrimitiveDescription& aOther) const
+{
+ return mType == aOther.mType &&
+ mFilterPrimitiveSubregion.IsEqualInterior(aOther.mFilterPrimitiveSubregion) &&
+ mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
+ mOutputColorSpace == aOther.mOutputColorSpace &&
+ mIsTainted == aOther.mIsTainted &&
+ mInputPrimitives == aOther.mInputPrimitives &&
+ mInputColorSpaces == aOther.mInputColorSpaces &&
+ mAttributes == aOther.mAttributes;
+}
+
+// FilterDescription
+
+bool
+FilterDescription::operator==(const FilterDescription& aOther) const
+{
+ return mPrimitives == aOther.mPrimitives;
+}
+
+// AttributeMap
+
+// A class that wraps different types for easy storage in a hashtable. Only
+// used by AttributeMap.
+struct FilterAttribute {
+ FilterAttribute(const FilterAttribute& aOther);
+ ~FilterAttribute();
+
+ bool operator==(const FilterAttribute& aOther) const;
+ bool operator!=(const FilterAttribute& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ AttributeType Type() const { return mType; }
+
+#define MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(type, typeLabel) \
+ explicit FilterAttribute(type aValue) \
+ : mType(AttributeType::e##typeLabel), m##typeLabel(aValue) \
+ {} \
+ type As##typeLabel() { \
+ MOZ_ASSERT(mType == AttributeType::e##typeLabel); \
+ return m##typeLabel; \
+ }
+
+#define MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(className) \
+ explicit FilterAttribute(const className& aValue) \
+ : mType(AttributeType::e##className), m##className(new className(aValue)) \
+ {} \
+ className As##className() { \
+ MOZ_ASSERT(mType == AttributeType::e##className); \
+ return *m##className; \
+ }
+
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(bool, Bool)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(uint32_t, Uint)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(float, Float)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Size)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntSize)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntPoint)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix5x4)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Point3D)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Color)
+ MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(AttributeMap)
+
+#undef MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC
+#undef MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS
+
+ FilterAttribute(const float* aValue, uint32_t aLength)
+ : mType(AttributeType::eFloats)
+ {
+ mFloats = new nsTArray<float>();
+ mFloats->AppendElements(aValue, aLength);
+ }
+
+ const nsTArray<float>& AsFloats() const {
+ MOZ_ASSERT(mType == AttributeType::eFloats);
+ return *mFloats;
+ }
+
+private:
+ const AttributeType mType;
+
+ union {
+ bool mBool;
+ uint32_t mUint;
+ float mFloat;
+ Size* mSize;
+ IntSize* mIntSize;
+ IntPoint* mIntPoint;
+ Matrix* mMatrix;
+ Matrix5x4* mMatrix5x4;
+ Point3D* mPoint3D;
+ Color* mColor;
+ AttributeMap* mAttributeMap;
+ nsTArray<float>* mFloats;
+ };
+};
+
+FilterAttribute::FilterAttribute(const FilterAttribute& aOther)
+ : mType(aOther.mType)
+{
+ switch (mType) {
+ case AttributeType::eBool:
+ mBool = aOther.mBool;
+ break;
+ case AttributeType::eUint:
+ mUint = aOther.mUint;
+ break;
+ case AttributeType::eFloat:
+ mFloat = aOther.mFloat;
+ break;
+
+#define HANDLE_CLASS(className) \
+ case AttributeType::e##className: \
+ m##className = new className(*aOther.m##className); \
+ break;
+
+ HANDLE_CLASS(Size)
+ HANDLE_CLASS(IntSize)
+ HANDLE_CLASS(IntPoint)
+ HANDLE_CLASS(Matrix)
+ HANDLE_CLASS(Matrix5x4)
+ HANDLE_CLASS(Point3D)
+ HANDLE_CLASS(Color)
+ HANDLE_CLASS(AttributeMap)
+
+#undef HANDLE_CLASS
+
+ case AttributeType::eFloats:
+ mFloats = new nsTArray<float>(*aOther.mFloats);
+ break;
+ case AttributeType::Max:
+ break;
+ }
+}
+
+FilterAttribute::~FilterAttribute() {
+ switch (mType) {
+ case AttributeType::Max:
+ case AttributeType::eBool:
+ case AttributeType::eUint:
+ case AttributeType::eFloat:
+ break;
+
+#define HANDLE_CLASS(className) \
+ case AttributeType::e##className: \
+ delete m##className; \
+ break;
+
+ HANDLE_CLASS(Size)
+ HANDLE_CLASS(IntSize)
+ HANDLE_CLASS(IntPoint)
+ HANDLE_CLASS(Matrix)
+ HANDLE_CLASS(Matrix5x4)
+ HANDLE_CLASS(Point3D)
+ HANDLE_CLASS(Color)
+ HANDLE_CLASS(AttributeMap)
+
+#undef HANDLE_CLASS
+
+ case AttributeType::eFloats:
+ delete mFloats;
+ break;
+ }
+}
+
+bool
+FilterAttribute::operator==(const FilterAttribute& aOther) const
+{
+ if (mType != aOther.mType) {
+ return false;
+ }
+
+ switch (mType) {
+
+#define HANDLE_TYPE(typeName) \
+ case AttributeType::e##typeName: \
+ return m##typeName == aOther.m##typeName;
+
+ HANDLE_TYPE(Bool)
+ HANDLE_TYPE(Uint)
+ HANDLE_TYPE(Float)
+ HANDLE_TYPE(Size)
+ HANDLE_TYPE(IntSize)
+ HANDLE_TYPE(IntPoint)
+ HANDLE_TYPE(Matrix)
+ HANDLE_TYPE(Matrix5x4)
+ HANDLE_TYPE(Point3D)
+ HANDLE_TYPE(Color)
+ HANDLE_TYPE(AttributeMap)
+ HANDLE_TYPE(Floats)
+
+#undef HANDLE_TYPE
+
+ default:
+ return false;
+ }
+}
+
+typedef FilterAttribute Attribute;
+
+AttributeMap::AttributeMap()
+{
+}
+
+AttributeMap::~AttributeMap()
+{
+}
+
+AttributeMap::AttributeMap(const AttributeMap& aOther)
+{
+ for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& attributeName = iter.Key();
+ Attribute* attribute = iter.UserData();
+ mMap.Put(attributeName, new Attribute(*attribute));
+ }
+}
+
+AttributeMap&
+AttributeMap::operator=(const AttributeMap& aOther)
+{
+ if (this != &aOther) {
+ mMap.Clear();
+ for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& attributeName = iter.Key();
+ Attribute* attribute = iter.UserData();
+ mMap.Put(attributeName, new Attribute(*attribute));
+ }
+ }
+ return *this;
+}
+
+bool
+AttributeMap::operator==(const AttributeMap& aOther) const
+{
+ if (mMap.Count() != aOther.mMap.Count()) {
+ return false;
+ }
+
+ for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& attributeName = iter.Key();
+ Attribute* attribute = iter.UserData();
+ Attribute* matchingAttribute = mMap.Get(attributeName);
+ if (!matchingAttribute || *matchingAttribute != *attribute) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+uint32_t
+AttributeMap::Count() const
+{
+ return mMap.Count();
+}
+
+nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator
+AttributeMap::ConstIter() const
+{
+ return mMap.ConstIter();
+}
+
+/* static */ AttributeType
+AttributeMap::GetType(FilterAttribute* aAttribute)
+{
+ return aAttribute->Type();
+}
+
+#define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \
+ type \
+ AttributeMap::Get##typeLabel(AttributeName aName) const { \
+ Attribute* value = mMap.Get(aName); \
+ return value ? value->As##typeLabel() : defaultValue; \
+ } \
+ void \
+ AttributeMap::Set(AttributeName aName, type aValue) { \
+ mMap.Remove(aName); \
+ mMap.Put(aName, new Attribute(aValue)); \
+ }
+
+#define MAKE_ATTRIBUTE_HANDLERS_CLASS(className) \
+ className \
+ AttributeMap::Get##className(AttributeName aName) const { \
+ Attribute* value = mMap.Get(aName); \
+ return value ? value->As##className() : className(); \
+ } \
+ void \
+ AttributeMap::Set(AttributeName aName, const className& aValue) { \
+ mMap.Remove(aName); \
+ mMap.Put(aName, new Attribute(aValue)); \
+ }
+
+MAKE_ATTRIBUTE_HANDLERS_BASIC(bool, Bool, false)
+MAKE_ATTRIBUTE_HANDLERS_BASIC(uint32_t, Uint, 0)
+MAKE_ATTRIBUTE_HANDLERS_BASIC(float, Float, 0)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Size)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(IntSize)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(IntPoint)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix5x4)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Point3D)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(Color)
+MAKE_ATTRIBUTE_HANDLERS_CLASS(AttributeMap)
+
+#undef MAKE_ATTRIBUTE_HANDLERS_BASIC
+#undef MAKE_ATTRIBUTE_HANDLERS_CLASS
+
+const nsTArray<float>&
+AttributeMap::GetFloats(AttributeName aName) const
+{
+ Attribute* value = mMap.Get(aName);
+ if (!value) {
+ value = new Attribute(nullptr, 0);
+ mMap.Put(aName, value);
+ }
+ return value->AsFloats();
+}
+
+void
+AttributeMap::Set(AttributeName aName, const float* aValues, int32_t aLength)
+{
+ mMap.Remove(aName);
+ mMap.Put(aName, new Attribute(aValues, aLength));
+}
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/src/FilterSupport.h b/gfx/src/FilterSupport.h
new file mode 100644
index 000000000..96a43d7cb
--- /dev/null
+++ b/gfx/src/FilterSupport.h
@@ -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
diff --git a/gfx/src/PingPongRegion.h b/gfx/src/PingPongRegion.h
new file mode 100644
index 000000000..d3bdcae1b
--- /dev/null
+++ b/gfx/src/PingPongRegion.h
@@ -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
diff --git a/gfx/src/RegionBuilder.h b/gfx/src/RegionBuilder.h
new file mode 100644
index 000000000..c8bcd21b1
--- /dev/null
+++ b/gfx/src/RegionBuilder.h
@@ -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__
diff --git a/gfx/src/TiledRegion.cpp b/gfx/src/TiledRegion.cpp
new file mode 100644
index 000000000..efaa2346b
--- /dev/null
+++ b/gfx/src/TiledRegion.cpp
@@ -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
diff --git a/gfx/src/TiledRegion.h b/gfx/src/TiledRegion.h
new file mode 100644
index 000000000..051773241
--- /dev/null
+++ b/gfx/src/TiledRegion.h
@@ -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_ */
diff --git a/gfx/src/X11UndefineNone.h b/gfx/src/X11UndefineNone.h
new file mode 100644
index 000000000..638bd0419
--- /dev/null
+++ b/gfx/src/X11UndefineNone.h
@@ -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
+
diff --git a/gfx/src/X11Util.cpp b/gfx/src/X11Util.cpp
new file mode 100644
index 000000000..7dd02d2fd
--- /dev/null
+++ b/gfx/src/X11Util.cpp
@@ -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
diff --git a/gfx/src/X11Util.h b/gfx/src/X11Util.h
new file mode 100644
index 000000000..e04913342
--- /dev/null
+++ b/gfx/src/X11Util.h
@@ -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
diff --git a/gfx/src/gfxCrashReporterUtils.cpp b/gfx/src/gfxCrashReporterUtils.cpp
new file mode 100644
index 000000000..42647ccc6
--- /dev/null
+++ b/gfx/src/gfxCrashReporterUtils.cpp
@@ -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
diff --git a/gfx/src/gfxCrashReporterUtils.h b/gfx/src/gfxCrashReporterUtils.h
new file mode 100644
index 000000000..efc5ab1ab
--- /dev/null
+++ b/gfx/src/gfxCrashReporterUtils.h
@@ -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__
diff --git a/gfx/src/gfxTelemetry.cpp b/gfx/src/gfxTelemetry.cpp
new file mode 100644
index 000000000..9ea15dcbd
--- /dev/null
+++ b/gfx/src/gfxTelemetry.cpp
@@ -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
diff --git a/gfx/src/gfxTelemetry.h b/gfx/src/gfxTelemetry.h
new file mode 100644
index 000000000..9f0f618f0
--- /dev/null
+++ b/gfx/src/gfxTelemetry.h
@@ -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__
diff --git a/gfx/src/moz.build b/gfx/src/moz.build
new file mode 100644
index 000000000..3678eee7a
--- /dev/null
+++ b/gfx/src/moz.build
@@ -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']
diff --git a/gfx/src/nsBoundingMetrics.h b/gfx/src/nsBoundingMetrics.h
new file mode 100644
index 000000000..4665db24f
--- /dev/null
+++ b/gfx/src/nsBoundingMetrics.h
@@ -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
diff --git a/gfx/src/nsColor.cpp b/gfx/src/nsColor.cpp
new file mode 100644
index 000000000..359f9fde4
--- /dev/null
+++ b/gfx/src/nsColor.cpp
@@ -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;
+}
diff --git a/gfx/src/nsColor.h b/gfx/src/nsColor.h
new file mode 100644
index 000000000..2f21c91bf
--- /dev/null
+++ b/gfx/src/nsColor.h
@@ -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___ */
diff --git a/gfx/src/nsColorNameList.h b/gfx/src/nsColorNameList.h
new file mode 100644
index 000000000..b679b981d
--- /dev/null
+++ b/gfx/src/nsColorNameList.h
@@ -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))
+
diff --git a/gfx/src/nsColorNames.h b/gfx/src/nsColorNames.h
new file mode 100644
index 000000000..388789710
--- /dev/null
+++ b/gfx/src/nsColorNames.h
@@ -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___ */
diff --git a/gfx/src/nsCoord.h b/gfx/src/nsCoord.h
new file mode 100644
index 000000000..bb53611cf
--- /dev/null
+++ b/gfx/src/nsCoord.h
@@ -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 */
diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp
new file mode 100644
index 000000000..8a5c16973
--- /dev/null
+++ b/gfx/src/nsDeviceContext.cpp
@@ -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);
+}
diff --git a/gfx/src/nsDeviceContext.h b/gfx/src/nsDeviceContext.h
new file mode 100644
index 000000000..1115757eb
--- /dev/null
+++ b/gfx/src/nsDeviceContext.h
@@ -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_ */
diff --git a/gfx/src/nsFont.cpp b/gfx/src/nsFont.cpp
new file mode 100644
index 000000000..c5b1f09f0
--- /dev/null
+++ b/gfx/src/nsFont.cpp
@@ -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;
+ }
+}
diff --git a/gfx/src/nsFont.h b/gfx/src/nsFont.h
new file mode 100644
index 000000000..d21ce2593
--- /dev/null
+++ b/gfx/src/nsFont.h
@@ -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___ */
diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp
new file mode 100644
index 000000000..062bc73a2
--- /dev/null
+++ b/gfx/src/nsFontMetrics.cpp
@@ -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);
+}
+
diff --git a/gfx/src/nsFontMetrics.h b/gfx/src/nsFontMetrics.h
new file mode 100644
index 000000000..f8a2c5b93
--- /dev/null
+++ b/gfx/src/nsFontMetrics.h
@@ -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__ */
diff --git a/gfx/src/nsGfxCIID.h b/gfx/src/nsGfxCIID.h
new file mode 100644
index 000000000..37cc27d0d
--- /dev/null
+++ b/gfx/src/nsGfxCIID.h
@@ -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
diff --git a/gfx/src/nsIFontEnumerator.idl b/gfx/src/nsIFontEnumerator.idl
new file mode 100644
index 000000000..f5f4c6468
--- /dev/null
+++ b/gfx/src/nsIFontEnumerator.idl
@@ -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);
+};
diff --git a/gfx/src/nsIScriptableRegion.idl b/gfx/src/nsIScriptableRegion.idl
new file mode 100644
index 000000000..1e32fdd1b
--- /dev/null
+++ b/gfx/src/nsIScriptableRegion.idl
@@ -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;
+
+};
diff --git a/gfx/src/nsITheme.h b/gfx/src/nsITheme.h
new file mode 100644
index 000000000..9456a7394
--- /dev/null
+++ b/gfx/src/nsITheme.h
@@ -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
diff --git a/gfx/src/nsMargin.h b/gfx/src/nsMargin.h
new file mode 100644
index 000000000..d9b0b8bd9
--- /dev/null
+++ b/gfx/src/nsMargin.h
@@ -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 */
diff --git a/gfx/src/nsPoint.h b/gfx/src/nsPoint.h
new file mode 100644
index 000000000..b377eb5a5
--- /dev/null
+++ b/gfx/src/nsPoint.h
@@ -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 */
diff --git a/gfx/src/nsRect.cpp b/gfx/src/nsRect.cpp
new file mode 100644
index 000000000..c17c249b2
--- /dev/null
+++ b/gfx/src/nsRect.cpp
@@ -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
diff --git a/gfx/src/nsRect.h b/gfx/src/nsRect.h
new file mode 100644
index 000000000..267f5849c
--- /dev/null
+++ b/gfx/src/nsRect.h
@@ -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 */
diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp
new file mode 100644
index 000000000..3b0bec1e3
--- /dev/null
+++ b/gfx/src/nsRegion.cpp
@@ -0,0 +1,1146 @@
+/* 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 "nsRegion.h"
+#include "nsTArray.h"
+#include "gfxUtils.h"
+#include "mozilla/ToString.h"
+
+bool nsRegion::Contains(const nsRegion& aRgn) const
+{
+ // XXX this could be made faster by iterating over
+ // both regions at the same time some how
+ for (auto iter = aRgn.RectIter(); !iter.Done(); iter.Next()) {
+ if (!Contains(iter.Get())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool nsRegion::Intersects(const nsRect& aRect) const
+{
+ // XXX this could be made faster by using pixman_region32_contains_rect
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ if (iter.Get().Intersects(aRect)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void nsRegion::Inflate(const nsMargin& aMargin)
+{
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect.Inflate(aMargin);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+}
+
+void nsRegion::SimplifyOutward (uint32_t aMaxRects)
+{
+ MOZ_ASSERT(aMaxRects >= 1, "Invalid max rect count");
+
+ if (GetNumRects() <= aMaxRects)
+ return;
+
+ pixman_box32_t *boxes;
+ int n;
+ boxes = pixman_region32_rectangles(&mImpl, &n);
+
+ // Try combining rects in horizontal bands into a single rect
+ int dest = 0;
+ for (int src = 1; src < n; src++)
+ {
+ // The goal here is to try to keep groups of rectangles that are vertically
+ // discontiguous as separate rectangles in the final region. This is
+ // simple and fast to implement and page contents tend to vary more
+ // vertically than horizontally (which is why our rectangles are stored
+ // sorted by y-coordinate, too).
+ //
+ // Note: if boxes share y1 because of the canonical representation they
+ // will share y2
+ while ((src < (n)) && boxes[dest].y1 == boxes[src].y1) {
+ // merge box[i] and box[i+1]
+ boxes[dest].x2 = boxes[src].x2;
+ src++;
+ }
+ if (src < n) {
+ dest++;
+ boxes[dest] = boxes[src];
+ }
+ }
+
+ uint32_t reducedCount = dest+1;
+ // pixman has a special representation for
+ // regions of 1 rectangle. So just use the
+ // bounds in that case
+ if (reducedCount > 1 && reducedCount <= aMaxRects) {
+ // reach into pixman and lower the number
+ // of rects stored in data.
+ mImpl.data->numRects = reducedCount;
+ } else {
+ *this = GetBounds();
+ }
+}
+
+// compute the covered area difference between two rows.
+// by iterating over both rows simultaneously and adding up
+// the additional increase in area caused by extending each
+// of the rectangles to the combined height of both rows
+static uint32_t ComputeMergedAreaIncrease(pixman_box32_t *topRects,
+ pixman_box32_t *topRectsEnd,
+ pixman_box32_t *bottomRects,
+ pixman_box32_t *bottomRectsEnd)
+{
+ uint32_t totalArea = 0;
+ struct pt {
+ int32_t x, y;
+ };
+
+
+ pt *i = (pt*)topRects;
+ pt *end_i = (pt*)topRectsEnd;
+ pt *j = (pt*)bottomRects;
+ pt *end_j = (pt*)bottomRectsEnd;
+ bool top = false;
+ bool bottom = false;
+
+ int cur_x = i->x;
+ bool top_next = top;
+ bool bottom_next = bottom;
+ //XXX: we could probably simplify this condition and perhaps move it into the loop below
+ if (j->x < cur_x) {
+ cur_x = j->x;
+ j++;
+ bottom_next = !bottom;
+ } else if (j->x == cur_x) {
+ i++;
+ top_next = !top;
+ bottom_next = !bottom;
+ j++;
+ } else {
+ top_next = !top;
+ i++;
+ }
+
+ int topRectsHeight = topRects->y2 - topRects->y1;
+ int bottomRectsHeight = bottomRects->y2 - bottomRects->y1;
+ int inbetweenHeight = bottomRects->y1 - topRects->y2;
+ int width = cur_x;
+ // top and bottom are the in-status to the left of cur_x
+ do {
+ if (top && !bottom) {
+ totalArea += (inbetweenHeight+bottomRectsHeight)*width;
+ } else if (bottom && !top) {
+ totalArea += (inbetweenHeight+topRectsHeight)*width;
+ } else if (bottom && top) {
+ totalArea += (inbetweenHeight)*width;
+ }
+ top = top_next;
+ bottom = bottom_next;
+ // find the next edge
+ if (i->x < j->x) {
+ top_next = !top;
+ width = i->x - cur_x;
+ cur_x = i->x;
+ i++;
+ } else if (j->x < i->x) {
+ bottom_next = !bottom;
+ width = j->x - cur_x;
+ cur_x = j->x;
+ j++;
+ } else { // i->x == j->x
+ top_next = !top;
+ bottom_next = !bottom;
+ width = i->x - cur_x;
+ cur_x = i->x;
+ i++;
+ j++;
+ }
+ } while (i < end_i && j < end_j);
+
+ // handle any remaining rects
+ while (i < end_i) {
+ width = i->x - cur_x;
+ cur_x = i->x;
+ i++;
+ if (top)
+ totalArea += (inbetweenHeight+bottomRectsHeight)*width;
+ top = !top;
+ }
+
+ while (j < end_j) {
+ width = j->x - cur_x;
+ cur_x = j->x;
+ j++;
+ if (bottom)
+ totalArea += (inbetweenHeight+topRectsHeight)*width;
+ bottom = !bottom;
+ }
+ return totalArea;
+}
+
+static pixman_box32_t *
+CopyRow(pixman_box32_t *dest_it, pixman_box32_t *src_start, pixman_box32_t *src_end)
+{
+ // XXX: std::copy
+ pixman_box32_t *src_it = src_start;
+ while (src_it < src_end) {
+ *dest_it++ = *src_it++;
+ }
+ return dest_it;
+}
+
+
+#define WRITE_RECT(x1, x2, y1, y2) \
+ do { \
+ tmpRect->x1 = x1; \
+ tmpRect->x2 = x2; \
+ tmpRect->y1 = y1; \
+ tmpRect->y2 = y2; \
+ tmpRect++; \
+ } while (0)
+
+/* If 'r' overlaps the current rect, then expand the current rect to include
+ * it. Otherwise write the current rect out to tmpRect, and set r as the
+ * updated current rect. */
+#define MERGE_RECT(r) \
+ do { \
+ if (r->x1 <= x2) { \
+ if (x2 < r->x2) \
+ x2 = r->x2; \
+ } else { \
+ WRITE_RECT(x1, x2, y1, y2); \
+ x1 = r->x1; \
+ x2 = r->x2; \
+ } \
+ r++; \
+ } while (0)
+
+
+/* Can we merge two sets of rects without extra space?
+ * Yes, but not easily. We can even do it stably
+ * but we don't need that property.
+ *
+ * This is written in the style of pixman_region_union_o */
+static pixman_box32_t *
+MergeRects(pixman_box32_t *r1,
+ pixman_box32_t *r1_end,
+ pixman_box32_t *r2,
+ pixman_box32_t *r2_end,
+ pixman_box32_t *tmpRect)
+{
+ /* This routine works by maintaining the current
+ * rectangle in x1,x2,y1,y2 and either merging
+ * in the left most rectangle if it overlaps or
+ * outputing the current rectangle and setting
+ * it to the the left most one */
+ const int y1 = r1->y1;
+ const int y2 = r2->y2;
+ int x1;
+ int x2;
+
+ /* Find the left-most edge */
+ if (r1->x1 < r2->x1) {
+ x1 = r1->x1;
+ x2 = r1->x2;
+ r1++;
+ } else {
+ x1 = r2->x1;
+ x2 = r2->x2;
+ r2++;
+ }
+
+ while (r1 != r1_end && r2 != r2_end) {
+ /* Find and merge the left-most rectangle */
+ if (r1->x1 < r2->x1)
+ MERGE_RECT (r1);
+ else
+ MERGE_RECT (r2);
+ }
+
+ /* Finish up any left overs */
+ if (r1 != r1_end) {
+ do {
+ MERGE_RECT (r1);
+ } while (r1 != r1_end);
+ } else if (r2 != r2_end) {
+ do {
+ MERGE_RECT(r2);
+ } while (r2 != r2_end);
+ }
+
+ /* Finish up the last rectangle */
+ WRITE_RECT(x1, x2, y1, y2);
+
+ return tmpRect;
+}
+
+void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold)
+{
+
+ pixman_box32_t *boxes;
+ int n;
+ boxes = pixman_region32_rectangles(&mImpl, &n);
+
+ // if we have no rectangles then we're done
+ if (!n)
+ return;
+
+ pixman_box32_t *end = boxes + n;
+ pixman_box32_t *topRectsEnd = boxes+1;
+ pixman_box32_t *topRects = boxes;
+
+ // we need some temporary storage for merging both rows of rectangles
+ AutoTArray<pixman_box32_t, 10> tmpStorage;
+ tmpStorage.SetCapacity(n);
+ pixman_box32_t *tmpRect = tmpStorage.Elements();
+
+ pixman_box32_t *destRect = boxes;
+ pixman_box32_t *rect = tmpRect;
+ // find the end of the first span of rectangles
+ while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
+ topRectsEnd++;
+ }
+
+ // if we only have one row we are done
+ if (topRectsEnd == end)
+ return;
+
+ pixman_box32_t *bottomRects = topRectsEnd;
+ pixman_box32_t *bottomRectsEnd = bottomRects+1;
+ do {
+ // find the end of the bottom span of rectangles
+ while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
+ bottomRectsEnd++;
+ }
+ uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd,
+ bottomRects, bottomRectsEnd);
+
+ if (totalArea <= aThreshold) {
+ // merge the rects into tmpRect
+ rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect);
+
+ // set topRects to where the newly merged rects will be so that we use them
+ // as our next set of topRects
+ topRects = destRect;
+ // copy the merged rects back into the destination
+ topRectsEnd = CopyRow(destRect, tmpRect, rect);
+ } else {
+ // copy the unmerged rects
+ destRect = CopyRow(destRect, topRects, topRectsEnd);
+
+ topRects = bottomRects;
+ topRectsEnd = bottomRectsEnd;
+ if (bottomRectsEnd == end) {
+ // copy the last row when we are done
+ topRectsEnd = CopyRow(destRect, topRects, topRectsEnd);
+ }
+ }
+ bottomRects = bottomRectsEnd;
+ } while (bottomRectsEnd != end);
+
+
+ uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n);
+ // pixman has a special representation for
+ // regions of 1 rectangle. So just use the
+ // bounds in that case
+ if (reducedCount > 1) {
+ // reach into pixman and lower the number
+ // of rects stored in data.
+ this->mImpl.data->numRects = reducedCount;
+ } else {
+ *this = GetBounds();
+ }
+}
+
+
+typedef void (*visit_fn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
+
+static bool VisitNextEdgeBetweenRect(visit_fn visit, void *closure, VisitSide side,
+ pixman_box32_t *&r1, pixman_box32_t *&r2, const int y, int &x1)
+{
+ // check for overlap
+ if (r1->x2 >= r2->x1) {
+ MOZ_ASSERT(r2->x1 >= x1);
+ visit(closure, side, x1, y, r2->x1, y);
+
+ // find the rect that ends first or always drop the one that comes first?
+ if (r1->x2 < r2->x2) {
+ x1 = r1->x2;
+ r1++;
+ } else {
+ x1 = r2->x2;
+ r2++;
+ }
+ return true;
+ } else {
+ MOZ_ASSERT(r1->x2 < r2->x2);
+ // we handle the corners by just extending the top and bottom edges
+ visit(closure, side, x1, y, r1->x2+1, y);
+ r1++;
+ // we assign x1 because we can assume that x1 <= r2->x1 - 1
+ // However the caller may know better and if so, may update
+ // x1 to r1->x1
+ x1 = r2->x1 - 1;
+ return false;
+ }
+}
+
+//XXX: if we need to this can compute the end of the row
+static void
+VisitSides(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end)
+{
+ // XXX: we can drop LEFT/RIGHT and just use the orientation
+ // of the line if it makes sense
+ while (r != r_end) {
+ visit(closure, VisitSide::LEFT, r->x1, r->y1, r->x1, r->y2);
+ visit(closure, VisitSide::RIGHT, r->x2, r->y1, r->x2, r->y2);
+ r++;
+ }
+}
+
+static void
+VisitAbove(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end)
+{
+ while (r != r_end) {
+ visit(closure, VisitSide::TOP, r->x1-1, r->y1, r->x2+1, r->y1);
+ r++;
+ }
+}
+
+static void
+VisitBelow(visit_fn visit, void *closure, pixman_box32_t *r, pixman_box32_t *r_end)
+{
+ while (r != r_end) {
+ visit(closure, VisitSide::BOTTOM, r->x1-1, r->y2, r->x2+1, r->y2);
+ r++;
+ }
+}
+
+static pixman_box32_t *
+VisitInbetween(visit_fn visit, void *closure, pixman_box32_t *r1,
+ pixman_box32_t *r1_end,
+ pixman_box32_t *r2,
+ pixman_box32_t *r2_end)
+{
+ const int y = r1->y2;
+ int x1;
+
+ bool overlap = false;
+ while (r1 != r1_end && r2 != r2_end) {
+ if (!overlap) {
+ /* Find the left-most edge */
+ if (r1->x1 < r2->x1) {
+ x1 = r1->x1 - 1;
+ } else {
+ x1 = r2->x1 - 1;
+ }
+ }
+
+ MOZ_ASSERT((x1 >= (r1->x1 - 1)) || (x1 >= (r2->x1 - 1)));
+ if (r1->x1 < r2->x1) {
+ overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::BOTTOM, r1, r2, y, x1);
+ } else {
+ overlap = VisitNextEdgeBetweenRect(visit, closure, VisitSide::TOP, r2, r1, y, x1);
+ }
+ }
+
+ /* Finish up which ever row has remaining rects*/
+ if (r1 != r1_end) {
+ // top row
+ do {
+ visit(closure, VisitSide::BOTTOM, x1, y, r1->x2 + 1, y);
+ r1++;
+ if (r1 == r1_end)
+ break;
+ x1 = r1->x1 - 1;
+ } while (true);
+ } else if (r2 != r2_end) {
+ // bottom row
+ do {
+ visit(closure, VisitSide::TOP, x1, y, r2->x2 + 1, y);
+ r2++;
+ if (r2 == r2_end)
+ break;
+ x1 = r2->x1 - 1;
+ } while (true);
+ }
+
+ return 0;
+}
+
+void nsRegion::VisitEdges (visit_fn visit, void *closure)
+{
+ pixman_box32_t *boxes;
+ int n;
+ boxes = pixman_region32_rectangles(&mImpl, &n);
+
+ // if we have no rectangles then we're done
+ if (!n)
+ return;
+
+ pixman_box32_t *end = boxes + n;
+ pixman_box32_t *topRectsEnd = boxes + 1;
+ pixman_box32_t *topRects = boxes;
+
+ // find the end of the first span of rectangles
+ while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
+ topRectsEnd++;
+ }
+
+ // In order to properly handle convex corners we always visit the sides first
+ // that way when we visit the corners we can pad using the value from the sides
+ VisitSides(visit, closure, topRects, topRectsEnd);
+
+ VisitAbove(visit, closure, topRects, topRectsEnd);
+
+ pixman_box32_t *bottomRects = topRects;
+ pixman_box32_t *bottomRectsEnd = topRectsEnd;
+ if (topRectsEnd != end) {
+ do {
+ // find the next row of rects
+ bottomRects = topRectsEnd;
+ bottomRectsEnd = topRectsEnd + 1;
+ while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
+ bottomRectsEnd++;
+ }
+
+ VisitSides(visit, closure, bottomRects, bottomRectsEnd);
+
+ if (topRects->y2 == bottomRects->y1) {
+ VisitInbetween(visit, closure, topRects, topRectsEnd,
+ bottomRects, bottomRectsEnd);
+ } else {
+ VisitBelow(visit, closure, topRects, topRectsEnd);
+ VisitAbove(visit, closure, bottomRects, bottomRectsEnd);
+ }
+
+ topRects = bottomRects;
+ topRectsEnd = bottomRectsEnd;
+ } while (bottomRectsEnd != end);
+ }
+
+ // the bottom of the region doesn't touch anything else so we
+ // can always visit it at the end
+ VisitBelow(visit, closure, bottomRects, bottomRectsEnd);
+}
+
+
+void nsRegion::SimplifyInward (uint32_t aMaxRects)
+{
+ NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
+
+ if (GetNumRects() <= aMaxRects)
+ return;
+
+ SetEmpty();
+}
+
+uint64_t nsRegion::Area () const
+{
+ uint64_t area = 0;
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& rect = iter.Get();
+ area += uint64_t(rect.width) * rect.height;
+ }
+ return area;
+}
+
+nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale)
+{
+ if (mozilla::gfx::FuzzyEqual(aXScale, 1.0f) &&
+ mozilla::gfx::FuzzyEqual(aYScale, 1.0f)) {
+ return *this;
+ }
+
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect.ScaleRoundOut(aXScale, aYScale);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+ return *this;
+}
+
+nsRegion& nsRegion::ScaleInverseRoundOut (float aXScale, float aYScale)
+{
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect.ScaleInverseRoundOut(aXScale, aYScale);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+ return *this;
+}
+
+static mozilla::gfx::IntRect
+TransformRect(const mozilla::gfx::IntRect& aRect, const mozilla::gfx::Matrix4x4& aTransform)
+{
+ if (aRect.IsEmpty()) {
+ return mozilla::gfx::IntRect();
+ }
+
+ mozilla::gfx::RectDouble rect(aRect.x, aRect.y, aRect.width, aRect.height);
+ rect = aTransform.TransformAndClipBounds(rect, mozilla::gfx::RectDouble::MaxIntRect());
+ rect.RoundOut();
+
+ mozilla::gfx::IntRect intRect;
+ if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
+ return mozilla::gfx::IntRect();
+ }
+
+ return intRect;
+}
+
+nsRegion& nsRegion::Transform (const mozilla::gfx::Matrix4x4 &aTransform)
+{
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ boxes[i] = RectToBox(nsIntRegion::ToRect(TransformRect(nsIntRegion::FromRect(rect), aTransform)));
+ }
+
+ pixman_region32_t region;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&region, boxes, n);
+
+ pixman_region32_fini(&mImpl);
+ mImpl = region;
+ return *this;
+}
+
+
+nsRegion nsRegion::ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP == aToAPP) {
+ return *this;
+ }
+
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect = rect.ScaleToOtherAppUnitsRoundOut(aFromAPP, aToAPP);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t pixmanRegion;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&pixmanRegion, boxes, n);
+
+ pixman_region32_fini(&region.mImpl);
+ region.mImpl = pixmanRegion;
+ return region;
+}
+
+nsRegion nsRegion::ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const
+{
+ if (aFromAPP == aToAPP) {
+ return *this;
+ }
+
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ rect = rect.ScaleToOtherAppUnitsRoundIn(aFromAPP, aToAPP);
+ boxes[i] = RectToBox(rect);
+ }
+
+ pixman_region32_t pixmanRegion;
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&pixmanRegion, boxes, n);
+
+ pixman_region32_fini(&region.mImpl);
+ region.mImpl = pixmanRegion;
+ return region;
+}
+
+nsIntRegion nsRegion::ToPixels (nscoord aAppUnitsPerPixel, bool aOutsidePixels) const
+{
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ mozilla::gfx::IntRect deviceRect;
+ if (aOutsidePixels)
+ deviceRect = rect.ToOutsidePixels(aAppUnitsPerPixel);
+ else
+ deviceRect = rect.ToNearestPixels(aAppUnitsPerPixel);
+
+ boxes[i] = RectToBox(deviceRect);
+ }
+
+ nsIntRegion intRegion;
+ pixman_region32_fini(&intRegion.mImpl.mImpl);
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n);
+
+ return intRegion;
+}
+
+nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
+{
+ return ToPixels(aAppUnitsPerPixel, true);
+}
+
+nsIntRegion nsRegion::ToNearestPixels (nscoord aAppUnitsPerPixel) const
+{
+ return ToPixels(aAppUnitsPerPixel, false);
+}
+
+nsIntRegion nsRegion::ScaleToNearestPixels (float aScaleX, float aScaleY,
+ nscoord aAppUnitsPerPixel) const
+{
+ nsIntRegion result;
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ mozilla::gfx::IntRect deviceRect =
+ iter.Get().ScaleToNearestPixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+ result.Or(result, deviceRect);
+ }
+ return result;
+}
+
+nsIntRegion nsRegion::ScaleToOutsidePixels (float aScaleX, float aScaleY,
+ nscoord aAppUnitsPerPixel) const
+{
+ // make a copy of the region so that we can mutate it inplace
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ boxes = pixman_region32_rectangles(&region.mImpl, &n);
+ for (int i=0; i<n; i++) {
+ nsRect rect = BoxToRect(boxes[i]);
+ mozilla::gfx::IntRect irect = rect.ScaleToOutsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+ boxes[i] = RectToBox(irect);
+ }
+
+ nsIntRegion iRegion;
+ // clear out the initial pixman_region so that we can replace it below
+ pixman_region32_fini(&iRegion.mImpl.mImpl);
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&iRegion.mImpl.mImpl, boxes, n);
+
+ return iRegion;
+}
+
+nsIntRegion nsRegion::ScaleToInsidePixels (float aScaleX, float aScaleY,
+ nscoord aAppUnitsPerPixel) const
+{
+ /* When scaling a rect, walk forward through the rect list up until the y value is greater
+ * than the current rect's YMost() value.
+ *
+ * For each rect found, check if the rects have a touching edge (in unscaled coordinates),
+ * and if one edge is entirely contained within the other.
+ *
+ * If it is, then the contained edge can be moved (in scaled pixels) to ensure that no
+ * gap exists.
+ *
+ * Since this could be potentially expensive - O(n^2), we only attempt this algorithm
+ * for the first rect.
+ */
+
+ // make a copy of this region so that we can mutate it in place
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
+
+ nsIntRegion intRegion;
+ if (n) {
+ nsRect first = BoxToRect(boxes[0]);
+ mozilla::gfx::IntRect firstDeviceRect =
+ first.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+
+ for (int i=1; i<n; i++) {
+ nsRect rect = nsRect(boxes[i].x1, boxes[i].y1,
+ boxes[i].x2 - boxes[i].x1,
+ boxes[i].y2 - boxes[i].y1);
+ mozilla::gfx::IntRect deviceRect =
+ rect.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
+
+ if (rect.y <= first.YMost()) {
+ if (rect.XMost() == first.x && rect.YMost() <= first.YMost()) {
+ // rect is touching on the left edge of the first rect and contained within
+ // the length of its left edge
+ deviceRect.SetRightEdge(firstDeviceRect.x);
+ } else if (rect.x == first.XMost() && rect.YMost() <= first.YMost()) {
+ // rect is touching on the right edge of the first rect and contained within
+ // the length of its right edge
+ deviceRect.SetLeftEdge(firstDeviceRect.XMost());
+ } else if (rect.y == first.YMost()) {
+ // The bottom of the first rect is on the same line as the top of rect, but
+ // they aren't necessarily contained.
+ if (rect.x <= first.x && rect.XMost() >= first.XMost()) {
+ // The top of rect contains the bottom of the first rect
+ firstDeviceRect.SetBottomEdge(deviceRect.y);
+ } else if (rect.x >= first.x && rect.XMost() <= first.XMost()) {
+ // The bottom of the first contains the top of rect
+ deviceRect.SetTopEdge(firstDeviceRect.YMost());
+ }
+ }
+ }
+
+ boxes[i] = RectToBox(deviceRect);
+ }
+
+ boxes[0] = RectToBox(firstDeviceRect);
+
+ pixman_region32_fini(&intRegion.mImpl.mImpl);
+ // This will union all of the rectangles and runs in about O(n lg(n))
+ pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n);
+ }
+ return intRegion;
+
+}
+
+// A cell's "value" is a pair consisting of
+// a) the area of the subrectangle it corresponds to, if it's in
+// aContainingRect and in the region, 0 otherwise
+// b) the area of the subrectangle it corresponds to, if it's in the region,
+// 0 otherwise
+// Addition, subtraction and identity are defined on these values in the
+// obvious way. Partial order is lexicographic.
+// A "large negative value" is defined with large negative numbers for both
+// fields of the pair. This negative value has the property that adding any
+// number of non-negative values to it always results in a negative value.
+//
+// The GetLargestRectangle algorithm works in three phases:
+// 1) Convert the region into a grid by adding vertical/horizontal lines for
+// each edge of each rectangle in the region.
+// 2) For each rectangle in the region, for each cell it contains, set that
+// cells's value as described above.
+// 3) Calculate the submatrix with the largest sum such that none of its cells
+// contain any 0s (empty regions). The rectangle represented by the
+// submatrix is the largest rectangle in the region.
+//
+// Let k be the number of rectangles in the region.
+// Let m be the height of the grid generated in step 1.
+// Let n be the width of the grid generated in step 1.
+//
+// Step 1 is O(k) in time and O(m+n) in space for the sparse grid.
+// Step 2 is O(mn) in time and O(mn) in additional space for the full grid.
+// Step 3 is O(m^2 n) in time and O(mn) in additional space
+//
+// The implementation of steps 1 and 2 are rather straightforward. However our
+// implementation of step 3 uses dynamic programming to achieve its efficiency.
+//
+// Psuedo code for step 3 is as follows where G is the grid from step 1 and A
+// is the array from step 2:
+// Phase3 = function (G, A, m, n) {
+// let (t,b,l,r,_) = MaxSum2D(A,m,n)
+// return rect(G[t],G[l],G[r],G[b]);
+// }
+// MaxSum2D = function (A, m, n) {
+// S = array(m+1,n+1)
+// S[0][i] = 0 for i in [0,n]
+// S[j][0] = 0 for j in [0,m]
+// S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1])
+// + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
+//
+// // top, bottom, left, right, area
+// var maxRect = (-1, -1, -1, -1, 0);
+//
+// for all (m',m'') in [0, m]^2 {
+// let B = { S[m'][i] - S[m''][i] | 0 <= i <= n }
+// let ((l,r),area) = MaxSum1D(B,n+1)
+// if (area > maxRect.area) {
+// maxRect := (m', m'', l, r, area)
+// }
+// }
+//
+// return maxRect;
+// }
+//
+// Originally taken from Improved algorithms for the k-maximum subarray problem
+// for small k - SE Bae, T Takaoka but modified to show the explicit tracking
+// of indices and we already have the prefix sums from our one call site so
+// there's no need to construct them.
+// MaxSum1D = function (A,n) {
+// var minIdx = 0;
+// var min = 0;
+// var maxIndices = (0,0);
+// var max = 0;
+// for i in range(n) {
+// let cand = A[i] - min;
+// if (cand > max) {
+// max := cand;
+// maxIndices := (minIdx, i)
+// }
+// if (min > A[i]) {
+// min := A[i];
+// minIdx := i;
+// }
+// }
+// return (minIdx, maxIdx, max);
+// }
+
+namespace {
+ // This class represents a partitioning of an axis delineated by coordinates.
+ // It internally maintains a sorted array of coordinates.
+ class AxisPartition {
+ public:
+ // Adds a new partition at the given coordinate to this partitioning. If
+ // the coordinate is already present in the partitioning, this does nothing.
+ void InsertCoord(nscoord c) {
+ uint32_t i = mStops.IndexOfFirstElementGt(c);
+ if (i == 0 || mStops[i-1] != c) {
+ mStops.InsertElementAt(i, c);
+ }
+ }
+
+ // Returns the array index of the given partition point. The partition
+ // point must already be present in the partitioning.
+ int32_t IndexOf(nscoord p) const {
+ return mStops.BinaryIndexOf(p);
+ }
+
+ // Returns the partition at the given index which must be non-zero and
+ // less than the number of partitions in this partitioning.
+ nscoord StopAt(int32_t index) const {
+ return mStops[index];
+ }
+
+ // Returns the size of the gap between the partition at the given index and
+ // the next partition in this partitioning. If the index is the last index
+ // in the partitioning, the result is undefined.
+ nscoord StopSize(int32_t index) const {
+ return mStops[index+1] - mStops[index];
+ }
+
+ // Returns the number of partitions in this partitioning.
+ int32_t GetNumStops() const { return mStops.Length(); }
+
+ private:
+ nsTArray<nscoord> mStops;
+ };
+
+ const int64_t kVeryLargeNegativeNumber = 0xffff000000000000ll;
+
+ struct SizePair {
+ int64_t mSizeContainingRect;
+ int64_t mSize;
+
+ SizePair() : mSizeContainingRect(0), mSize(0) {}
+
+ static SizePair VeryLargeNegative() {
+ SizePair result;
+ result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
+ return result;
+ }
+ bool operator<(const SizePair& aOther) const {
+ if (mSizeContainingRect < aOther.mSizeContainingRect)
+ return true;
+ if (mSizeContainingRect > aOther.mSizeContainingRect)
+ return false;
+ return mSize < aOther.mSize;
+ }
+ bool operator>(const SizePair& aOther) const {
+ return aOther.operator<(*this);
+ }
+ SizePair operator+(const SizePair& aOther) const {
+ SizePair result = *this;
+ result.mSizeContainingRect += aOther.mSizeContainingRect;
+ result.mSize += aOther.mSize;
+ return result;
+ }
+ SizePair operator-(const SizePair& aOther) const {
+ SizePair result = *this;
+ result.mSizeContainingRect -= aOther.mSizeContainingRect;
+ result.mSize -= aOther.mSize;
+ return result;
+ }
+ };
+
+ // Returns the sum and indices of the subarray with the maximum sum of the
+ // given array (A,n), assuming the array is already in prefix sum form.
+ SizePair MaxSum1D(const nsTArray<SizePair> &A, int32_t n,
+ int32_t *minIdx, int32_t *maxIdx) {
+ // The min/max indicies of the largest subarray found so far
+ SizePair min, max;
+ int32_t currentMinIdx = 0;
+
+ *minIdx = 0;
+ *maxIdx = 0;
+
+ // Because we're given the array in prefix sum form, we know the first
+ // element is 0
+ for(int32_t i = 1; i < n; i++) {
+ SizePair cand = A[i] - min;
+ if (cand > max) {
+ max = cand;
+ *minIdx = currentMinIdx;
+ *maxIdx = i;
+ }
+ if (min > A[i]) {
+ min = A[i];
+ currentMinIdx = i;
+ }
+ }
+
+ return max;
+ }
+} // namespace
+
+nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const {
+ nsRect bestRect;
+
+ if (GetNumRects() <= 1) {
+ bestRect = GetBounds();
+ return bestRect;
+ }
+
+ AxisPartition xaxis, yaxis;
+
+ // Step 1: Calculate the grid lines
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& rect = iter.Get();
+ xaxis.InsertCoord(rect.x);
+ xaxis.InsertCoord(rect.XMost());
+ yaxis.InsertCoord(rect.y);
+ yaxis.InsertCoord(rect.YMost());
+ }
+ if (!aContainingRect.IsEmpty()) {
+ xaxis.InsertCoord(aContainingRect.x);
+ xaxis.InsertCoord(aContainingRect.XMost());
+ yaxis.InsertCoord(aContainingRect.y);
+ yaxis.InsertCoord(aContainingRect.YMost());
+ }
+
+ // Step 2: Fill out the grid with the areas
+ // Note: due to the ordering of rectangles in the region, it is not always
+ // possible to combine steps 2 and 3 so we don't try to be clever.
+ int32_t matrixHeight = yaxis.GetNumStops() - 1;
+ int32_t matrixWidth = xaxis.GetNumStops() - 1;
+ int32_t matrixSize = matrixHeight * matrixWidth;
+ nsTArray<SizePair> areas(matrixSize);
+ areas.SetLength(matrixSize);
+
+ for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& rect = iter.Get();
+ int32_t xstart = xaxis.IndexOf(rect.x);
+ int32_t xend = xaxis.IndexOf(rect.XMost());
+ int32_t y = yaxis.IndexOf(rect.y);
+ int32_t yend = yaxis.IndexOf(rect.YMost());
+
+ for (; y < yend; y++) {
+ nscoord height = yaxis.StopSize(y);
+ for (int32_t x = xstart; x < xend; x++) {
+ nscoord width = xaxis.StopSize(x);
+ int64_t size = width*int64_t(height);
+ if (rect.Intersects(aContainingRect)) {
+ areas[y*matrixWidth+x].mSizeContainingRect = size;
+ }
+ areas[y*matrixWidth+x].mSize = size;
+ }
+ }
+ }
+
+ // Step 3: Find the maximum submatrix sum that does not contain a rectangle
+ {
+ // First get the prefix sum array
+ int32_t m = matrixHeight + 1;
+ int32_t n = matrixWidth + 1;
+ nsTArray<SizePair> pareas(m*n);
+ pareas.SetLength(m*n);
+ for (int32_t y = 1; y < m; y++) {
+ for (int32_t x = 1; x < n; x++) {
+ SizePair area = areas[(y-1)*matrixWidth+x-1];
+ if (!area.mSize) {
+ area = SizePair::VeryLargeNegative();
+ }
+ area = area + pareas[ y*n+x-1]
+ + pareas[(y-1)*n+x ]
+ - pareas[(y-1)*n+x-1];
+ pareas[y*n+x] = area;
+ }
+ }
+
+ // No longer need the grid
+ areas.SetLength(0);
+
+ SizePair bestArea;
+ struct {
+ int32_t left, top, right, bottom;
+ } bestRectIndices = { 0, 0, 0, 0 };
+ for (int32_t m1 = 0; m1 < m; m1++) {
+ for (int32_t m2 = m1+1; m2 < m; m2++) {
+ nsTArray<SizePair> B;
+ B.SetLength(n);
+ for (int32_t i = 0; i < n; i++) {
+ B[i] = pareas[m2*n+i] - pareas[m1*n+i];
+ }
+ int32_t minIdx, maxIdx;
+ SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
+ if (area > bestArea) {
+ bestRectIndices.left = minIdx;
+ bestRectIndices.top = m1;
+ bestRectIndices.right = maxIdx;
+ bestRectIndices.bottom = m2;
+ bestArea = area;
+ }
+ }
+ }
+
+ bestRect.MoveTo(xaxis.StopAt(bestRectIndices.left),
+ yaxis.StopAt(bestRectIndices.top));
+ bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.x,
+ yaxis.StopAt(bestRectIndices.bottom) - bestRect.y);
+ }
+
+ return bestRect;
+}
+
+std::ostream& operator<<(std::ostream& stream, const nsRegion& m) {
+ stream << "[";
+
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&m.mImpl), &n);
+ for (int i=0; i<n; i++) {
+ if (i != 0) {
+ stream << "; ";
+ }
+ stream << boxes[i].x1 << "," << boxes[i].y1 << "," << boxes[i].x2 << "," << boxes[i].y2;
+ }
+
+ stream << "]";
+ return stream;
+}
+
+nsCString
+nsRegion::ToString() const {
+ return nsCString(mozilla::ToString(*this).c_str());
+}
diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h
new file mode 100644
index 000000000..6f9f7fc8e
--- /dev/null
+++ b/gfx/src/nsRegion.h
@@ -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
diff --git a/gfx/src/nsRegionFwd.h b/gfx/src/nsRegionFwd.h
new file mode 100644
index 000000000..6d5345d14
--- /dev/null
+++ b/gfx/src/nsRegionFwd.h
@@ -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
diff --git a/gfx/src/nsRenderingContext.h b/gfx/src/nsRenderingContext.h
new file mode 100644
index 000000000..02bb397d6
--- /dev/null
+++ b/gfx/src/nsRenderingContext.h
@@ -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__
diff --git a/gfx/src/nsScriptableRegion.cpp b/gfx/src/nsScriptableRegion.cpp
new file mode 100644
index 000000000..f0afff8f4
--- /dev/null
+++ b/gfx/src/nsScriptableRegion.cpp
@@ -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;
+}
diff --git a/gfx/src/nsScriptableRegion.h b/gfx/src/nsScriptableRegion.h
new file mode 100644
index 000000000..094093c75
--- /dev/null
+++ b/gfx/src/nsScriptableRegion.h
@@ -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
diff --git a/gfx/src/nsSize.h b/gfx/src/nsSize.h
new file mode 100644
index 000000000..f27c478f9
--- /dev/null
+++ b/gfx/src/nsSize.h
@@ -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 */
diff --git a/gfx/src/nsThebesFontEnumerator.cpp b/gfx/src/nsThebesFontEnumerator.cpp
new file mode 100644
index 000000000..3ef3f443b
--- /dev/null
+++ b/gfx/src/nsThebesFontEnumerator.cpp
@@ -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;
+}
diff --git a/gfx/src/nsThebesFontEnumerator.h b/gfx/src/nsThebesFontEnumerator.h
new file mode 100644
index 000000000..666663e3a
--- /dev/null
+++ b/gfx/src/nsThebesFontEnumerator.h
@@ -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_ */
diff --git a/gfx/src/nsThebesGfxFactory.cpp b/gfx/src/nsThebesGfxFactory.cpp
new file mode 100644
index 000000000..8e1e18d5c
--- /dev/null
+++ b/gfx/src/nsThebesGfxFactory.cpp
@@ -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;
diff --git a/gfx/src/nsThemeConstants.h b/gfx/src/nsThemeConstants.h
new file mode 100644
index 000000000..7825b9c6f
--- /dev/null
+++ b/gfx/src/nsThemeConstants.h
@@ -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
diff --git a/gfx/src/nsTransform2D.cpp b/gfx/src/nsTransform2D.cpp
new file mode 100644
index 000000000..becea4392
--- /dev/null
+++ b/gfx/src/nsTransform2D.cpp
@@ -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;
+}
diff --git a/gfx/src/nsTransform2D.h b/gfx/src/nsTransform2D.h
new file mode 100644
index 000000000..8999b2cf3
--- /dev/null
+++ b/gfx/src/nsTransform2D.h
@@ -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