diff --git a/gfx/thebes/gfxColorManagement.cpp b/gfx/thebes/gfxColorManagement.cpp new file mode 100644 index 000000000000..616167cd10c7 --- /dev/null +++ b/gfx/thebes/gfxColorManagement.cpp @@ -0,0 +1,412 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfxColorManagement.h" +#include "mozilla/Preferences.h" +#include "mozilla/Mutex.h" +#include "nsCRTGlue.h" + +using namespace mozilla; + +gfxColorManagement* gfxColorManagement::sInstance = nullptr; + +// Avoid the typos in getting the name wrong +#define kCMSPrefNameForcesRGB "gfx.color_management.force_srgb" +#define kCMSPrefNameMode "gfx.color_management.mode" +#define kCMSPrefNameEnablev4 "gfx.color_management.enablev4" +#define kCMSPrefNameRenderingIntent "gfx.color_management.rendering_intent" +#define kCMSPrefNameDisplayProfile "gfx.color_management.display_profile" +#define kCMSObsoletePrefEnabled "gfx.color_management.enabled" + +// The color mangement preferences to watch +static const char* kObservedCMSPrefs[] = { + kCMSPrefNameForcesRGB, + kCMSPrefNameMode, + kCMSPrefNameEnablev4, + kCMSPrefNameRenderingIntent, + kCMSPrefNameDisplayProfile, + nullptr +}; + +/* Class to listen for pref changes so that chrome code can dynamically + force sRGB as an output profile. See Bug #452125. */ +class CMSPrefsObserver MOZ_FINAL : public nsIObserver, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER +}; + +NS_IMPL_ISUPPORTS2(CMSPrefsObserver, nsIObserver, nsISupportsWeakReference) + +NS_IMETHODIMP +CMSPrefsObserver::Observe(nsISupports*, const char*, const PRUnichar *aData) +{ + return gfxColorManagement::InstanceNC().PreferencesModified(aData); +} + +const gfxColorManagement& +gfxColorManagement::Instance() +{ + if (!sInstance) { + sInstance = new gfxColorManagement; + } + return *sInstance; +} + +gfxColorManagement& +gfxColorManagement::InstanceNC() +{ + if (!sInstance) { + sInstance = new gfxColorManagement; + } + return *sInstance; +} + +void +gfxColorManagement::Destroy() +{ + if (sInstance) { + delete sInstance; + sInstance = nullptr; + } +} + +bool +gfxColorManagement::Exists() +{ + return sInstance != nullptr; +} + +gfxColorManagement::gfxColorManagement() + : mPrefsObserver(nullptr), + mPrefLock(nullptr), + mPrefEnableV4(false), + mPrefForcesRGB(false), + mPrefMode(-1), + mPrefIntent(-1), + mPrefDisplayProfile(), +#ifdef DEBUG + mPrefsInitialized(false), +#endif + mModeSet(false), + mMode(eCMSMode_Off), + mIntent(-2), + mOutputProfile(nullptr), + msRGBProfile(nullptr), + mRGBTransform(nullptr), + mInverseRGBTransform(nullptr), + mRGBATransform(nullptr), + mDefaultPlatformProfile(nullptr) +{ + mPrefLock = new Mutex("gfxColorManagement::mPrefLock"); +} + +gfxColorManagement::~gfxColorManagement() +{ + // Unregister our CMS Override callback. + NS_ASSERTION(mPrefsObserver, "mPrefsObserver is already gone"); + if (mPrefsObserver) { + Preferences::RemoveObservers(mPrefsObserver, kObservedCMSPrefs); + mPrefsObserver = nullptr; + } + + ResetAll(); + delete mPrefLock; +} + +NS_IMETHODIMP +gfxColorManagement::PreferencesModified(const PRUnichar* aName) +{ + if (NS_strcmp(aName, NS_LITERAL_STRING(kCMSPrefNameForcesRGB).get())) { + mPrefForcesRGB = Preferences::GetBool(kCMSPrefNameForcesRGB, false); + ResetAll(); + + } else if (NS_strcmp(aName, NS_LITERAL_STRING(kCMSPrefNameMode).get())) { + mPrefMode = Preferences::GetInt(kCMSPrefNameMode, -1); + + } else if (NS_strcmp(aName, NS_LITERAL_STRING(kCMSPrefNameEnablev4).get())) { + mPrefEnableV4 = Preferences::GetBool(kCMSPrefNameEnablev4, false); + + } else if (NS_strcmp(aName, NS_LITERAL_STRING(kCMSPrefNameRenderingIntent).get())) { + mPrefIntent = Preferences::GetInt(kCMSPrefNameRenderingIntent, -1); + + } else if (NS_strcmp(aName, NS_LITERAL_STRING(kCMSPrefNameDisplayProfile).get())) { + MutexAutoLock autoLock(*mPrefLock); + mPrefDisplayProfile = Preferences::GetCString(kCMSPrefNameDisplayProfile); + } + return NS_OK; +} + +void gfxColorManagement::MigratePreferences() +{ + // Migrate from the boolean color_management.enabled pref - we now use + // color_management.mode. + if (Preferences::HasUserValue(kCMSObsoletePrefEnabled)) { + if (Preferences::GetBool(kCMSObsoletePrefEnabled, false)) { + Preferences::SetInt(kCMSPrefNameMode, static_cast(eCMSMode_All)); + } + Preferences::ClearUser(kCMSObsoletePrefEnabled); + } +} + +void gfxColorManagement::Initialize(qcms_profile* aDefaultPlatformProfile) +{ + mDefaultPlatformProfile = aDefaultPlatformProfile; + + // Migrate the old preferences first + MigratePreferences(); + + // Get the CMS preferences: + mPrefMode = Preferences::GetInt(kCMSPrefNameMode, -1); + mPrefEnableV4 = Preferences::GetBool(kCMSPrefNameEnablev4, false); + mPrefIntent = Preferences::GetInt(kCMSPrefNameRenderingIntent, -1); + mPrefForcesRGB = Preferences::GetBool(kCMSPrefNameForcesRGB, false); + { + // Paranoid, we likely won't need this in practice + MutexAutoLock autoLock(*mPrefLock); + mPrefDisplayProfile = Preferences::GetCString(kCMSPrefNameDisplayProfile); + } + + // Create and register the CMS Override observer. + mPrefsObserver = new CMSPrefsObserver; + Preferences::AddWeakObservers(mPrefsObserver, kObservedCMSPrefs); + +#ifdef DEBUG + mPrefsInitialized = true; +#endif +} + +void gfxColorManagement::ResetAll() +{ + if (mRGBTransform) { + qcms_transform_release(mRGBTransform); + mRGBTransform = nullptr; + } + if (mInverseRGBTransform) { + qcms_transform_release(mInverseRGBTransform); + mInverseRGBTransform = nullptr; + } + if (mRGBATransform) { + qcms_transform_release(mRGBATransform); + mRGBATransform = nullptr; + } + if (mOutputProfile) { + qcms_profile_release(mOutputProfile); + + // handle the aliased case + if (msRGBProfile == mOutputProfile) { + msRGBProfile = nullptr; + } + mOutputProfile = nullptr; + } + if (msRGBProfile) { + qcms_profile_release(msRGBProfile); + msRGBProfile = nullptr; + } + + // Reset the state variables + mIntent = -2; + mMode = eCMSMode_Off; + mModeSet = false; +} + +eCMSMode +gfxColorManagement::GetMode() const +{ + NS_ASSERTION(mPrefsInitialized, "Prefs have not been initialized"); + if (mModeSet == false) { + mModeSet = true; + + if ((mPrefMode >= 0) && (mPrefMode < eCMSMode_AllCount)) { + mMode = static_cast(mPrefMode); + } + + if (mPrefEnableV4) { + qcms_enable_iccv4(); + } + } + return mMode; +} + +int +gfxColorManagement::GetRenderingIntent() const +{ + NS_ASSERTION(mPrefsInitialized, "Prefs have not been initialized"); + if (mIntent == -2) { + if (mPrefIntent >= 0) { + /* If the pref is within range, use it as an override. */ + if ((mPrefIntent >= QCMS_INTENT_MIN) && (mPrefIntent <= QCMS_INTENT_MAX)) { + mIntent = mPrefIntent; + } + /* If the pref is out of range, use embedded profile. */ + else { + mIntent = -1; + } + } + /* If we didn't get a valid intent from prefs, use the default. */ + else { + mIntent = QCMS_INTENT_DEFAULT; + } + } + return mIntent; +} + +qcms_profile* +gfxColorManagement::GetOutputProfile() const +{ + NS_ASSERTION(mPrefsInitialized, "Prefs have not been initialized"); + if (!mOutputProfile) { + /* Determine if we're using the internal override to force sRGB as + an output profile for reftests. See Bug 452125. + + Note that we don't normally (outside of tests) set a + default value of this preference, which means nsIPrefBranch::GetBoolPref + will typically throw (and leave its out-param untouched). + */ + if (mPrefForcesRGB) { + mOutputProfile = GetsRGBProfile(); + } + + if (!mOutputProfile) { + MutexAutoLock autoLock(*mPrefLock); + if (!mPrefDisplayProfile.IsEmpty()) { + mOutputProfile = qcms_profile_from_path(mPrefDisplayProfile); + } + } + + if (!mOutputProfile) { + mOutputProfile = mDefaultPlatformProfile; + } + + /* Determine if the profile looks bogus. If so, close the profile + * and use sRGB instead. See bug 460629, */ + if (mOutputProfile && qcms_profile_is_bogus(mOutputProfile)) { + NS_ASSERTION(mOutputProfile != GetsRGBProfile(), + "Builtin sRGB profile tagged as bogus!!!"); + qcms_profile_release(mOutputProfile); + mOutputProfile = nullptr; + } + + if (!mOutputProfile) { + mOutputProfile = GetsRGBProfile(); + } + /* Precache the LUT16 Interpolations for the output profile. See + bug 444661 for details. */ + qcms_profile_precache_output_transform(mOutputProfile); + } + return mOutputProfile; +} + +qcms_profile * +gfxColorManagement::GetsRGBProfile() const +{ + if (!msRGBProfile) { + /* Create the profile using qcms. */ + msRGBProfile = qcms_profile_sRGB(); + } + return msRGBProfile; +} + +qcms_transform * +gfxColorManagement::GetRGBTransform() const +{ + if (!mRGBTransform) { + qcms_profile *inProfile, *outProfile; + outProfile = GetOutputProfile(); + inProfile = GetsRGBProfile(); + + if (!inProfile || !outProfile) { + return nullptr; + } + + mRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8, + outProfile, QCMS_DATA_RGB_8, + QCMS_INTENT_PERCEPTUAL); + } + return mRGBTransform; +} + +qcms_transform * +gfxColorManagement::GetInverseRGBTransform() const +{ + NS_ASSERTION(mPrefsInitialized, "Prefs have not been initialized"); + if (!mInverseRGBTransform) { + qcms_profile *inProfile, *outProfile; + inProfile = GetOutputProfile(); + outProfile = GetsRGBProfile(); + + if (!inProfile || !outProfile) { + return nullptr; + } + + mInverseRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8, + outProfile, QCMS_DATA_RGB_8, + QCMS_INTENT_PERCEPTUAL); + } + + return mInverseRGBTransform; +} + +qcms_transform * +gfxColorManagement::GetRGBATransform() const +{ + if (!mRGBATransform) { + qcms_profile *inProfile, *outProfile; + outProfile = GetOutputProfile(); + inProfile = GetsRGBProfile(); + + if (!inProfile || !outProfile) { + return nullptr; + } + + mRGBATransform = qcms_transform_create(inProfile, QCMS_DATA_RGBA_8, + outProfile, QCMS_DATA_RGBA_8, + QCMS_INTENT_PERCEPTUAL); + } + return mRGBATransform; +} + +void +gfxColorManagement::TransformPixel(const gfxRGBA& aIn, gfxRGBA& aOut, + qcms_transform* aTransform) const +{ + if (aTransform) { + /* we want the bytes in RGB order */ +#ifdef IS_LITTLE_ENDIAN + /* ABGR puts the bytes in |RGBA| order on little endian */ + uint32_t packed = aIn.Packed(gfxRGBA::PACKED_ABGR); + qcms_transform_data(aTransform, + (uint8_t *)&packed, (uint8_t *)&packed, + 1); + aOut.~gfxRGBA(); + new (&aOut) gfxRGBA(packed, gfxRGBA::PACKED_ABGR); +#else + /* ARGB puts the bytes in |ARGB| order on big endian */ + uint32_t packed = aIn.Packed(gfxRGBA::PACKED_ARGB); + /* add one to move past the alpha byte */ + qcms_transform_data(aTransform, + (uint8_t *)&packed + 1, (uint8_t *)&packed + 1, + 1); + aOut.~gfxRGBA(); + new (&aOut) gfxRGBA(packed, gfxRGBA::PACKED_ARGB); +#endif + } + + else if (&aOut != &aIn) + aOut = aIn; +} + +gfxColorManagement::gfxColorManagement(const gfxColorManagement&) +{ + NS_ASSERTION(false, "Should not be calling gfxColorManagement copy constructor"); +} + +gfxColorManagement& gfxColorManagement::operator=(const gfxColorManagement&) +{ + NS_ASSERTION(false, "Should not be calling gfxColorManagement::operator="); + return *this; +} diff --git a/gfx/thebes/gfxColorManagement.h b/gfx/thebes/gfxColorManagement.h new file mode 100644 index 000000000000..185d1c622552 --- /dev/null +++ b/gfx/thebes/gfxColorManagement.h @@ -0,0 +1,125 @@ +/* -*- 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 GFX_COLORMANAGEMENT_H +#define GFX_COLORMANAGEMENT_H + +#include "nsIObserver.h" +#include "nsCOMPtr.h" +#include "gfxColor.h" +#include "nsString.h" +#include "qcms.h" + +class gfxColorManagement; +namespace mozilla { +class Mutex; +} + +enum eCMSMode { + eCMSMode_Off = 0, // No color management + eCMSMode_All = 1, // Color manage everything + eCMSMode_TaggedOnly = 2, // Color manage tagged Images Only + eCMSMode_AllCount = 3 +}; + +/** + * Preferences, utilities and CMS profile management. These are "global". + * The first use should be calling gfxColorManagement::InstanceNC().Initialize() + * on the main thread. + */ +class gfxColorManagement MOZ_FINAL +{ +public: + /** + * Manage the singleton. Instance() will create it if it isn't there. + */ + static const gfxColorManagement& Instance(); + static void Destroy(); + static bool Exists(); + + /** + * This must be called on the main thread. + */ + void Initialize(qcms_profile* aDefaultPlatformProfile); + + /** + * Reset all non-pref values, release various qcms profiles and transforms + */ + void ResetAll(); + + /** + * The result of this may depend on the default platform profile set in + * the Initialize() method. + */ + qcms_profile* GetOutputProfile() const; + + /** + *The following are going to set the cached values when first called, after + * that reusing them until a relevant pref change or a ResetAll() call. + */ + eCMSMode GetMode() const; + int GetRenderingIntent() const; + + qcms_profile* GetsRGBProfile() const; + qcms_transform* GetRGBTransform() const; + qcms_transform* GetInverseRGBTransform() const; + qcms_transform* GetRGBATransform() const; + + /** + * Convert a pixel using a cms transform in an endian-aware manner. + * + * Sets aOut to aIn if aTransform is nullptr. + */ + void TransformPixel(const gfxRGBA& aIn, gfxRGBA& aOut, qcms_transform* aTransform) const; + +private: + nsCOMPtr mPrefsObserver; + mozilla::Mutex* mPrefLock; + bool mPrefEnableV4; + bool mPrefForcesRGB; + int32_t mPrefMode; + int32_t mPrefIntent; + nsAdoptingCString mPrefDisplayProfile; +#ifdef DEBUG + bool mPrefsInitialized; +#endif + + mutable bool mModeSet; + mutable eCMSMode mMode; + mutable int mIntent; + + // These two may point to the same profile + mutable qcms_profile *mOutputProfile; + mutable qcms_profile *msRGBProfile; + + mutable qcms_transform *mRGBTransform; + mutable qcms_transform *mInverseRGBTransform; + mutable qcms_transform *mRGBATransform; + + qcms_profile* mDefaultPlatformProfile; + +private: + static gfxColorManagement* sInstance; + + gfxColorManagement(); + ~gfxColorManagement(); + + // =delete would have been nice here, but it isn't supported by all compilers + // as of Sep 2013. + gfxColorManagement(const gfxColorManagement&); + gfxColorManagement& operator=(const gfxColorManagement&); + + void MigratePreferences(); + NS_IMETHODIMP PreferencesModified(const PRUnichar* prefName); + + // Only friends can get to the non-const version. This is a bit arbitrary + // as we currently don't have a reason to allow others access, but it isn't + // really a hard requirement. + friend class gfxPlatform; + friend class CMSPrefsObserver; + static gfxColorManagement& InstanceNC(); +}; + +#endif /* GFX_COLORMANAGEMENT_H */ diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 7c07c935b6fd..cabffc579231 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -16,6 +16,7 @@ #include "gfxContext.h" #include "gfxColor.h" +#include "gfxColorManagement.h" #include "gfxMatrix.h" #include "gfxASurface.h" #include "gfxPattern.h" @@ -1297,13 +1298,15 @@ gfxContext::ClipContainsRect(const gfxRect& aRect) void gfxContext::SetColor(const gfxRGBA& c) { + const gfxColorManagement& colorManagement = gfxColorManagement::Instance(); if (mCairo) { - if (gfxPlatform::GetCMSMode() == eCMSMode_All) { + if (colorManagement.GetMode() == eCMSMode_All) { gfxRGBA cms; - qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); - if (transform) - gfxPlatform::TransformPixel(c, cms, transform); + qcms_transform *transform = colorManagement.GetRGBTransform(); + if (transform) { + colorManagement.TransformPixel(c, cms, transform); + } // Use the original alpha to avoid unnecessary float->byte->float // conversion errors @@ -1316,12 +1319,13 @@ gfxContext::SetColor(const gfxRGBA& c) CurrentState().sourceSurfCairo = nullptr; CurrentState().sourceSurface = nullptr; - if (gfxPlatform::GetCMSMode() == eCMSMode_All) { + if (colorManagement.GetMode() == eCMSMode_All) { gfxRGBA cms; - qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); - if (transform) - gfxPlatform::TransformPixel(c, cms, transform); + qcms_transform *transform = colorManagement.GetRGBTransform(); + if (transform) { + colorManagement.TransformPixel(c, cms, transform); + } // Use the original alpha to avoid unnecessary float->byte->float // conversion errors diff --git a/gfx/thebes/gfxPattern.cpp b/gfx/thebes/gfxPattern.cpp index 7c3e5100e904..536de34cf6be 100644 --- a/gfx/thebes/gfxPattern.cpp +++ b/gfx/thebes/gfxPattern.cpp @@ -7,6 +7,7 @@ #include "gfxPattern.h" #include "gfxASurface.h" #include "gfxPlatform.h" +#include "gfxColorManagement.h" #include "cairo.h" @@ -79,11 +80,13 @@ gfxPattern::AddColorStop(gfxFloat offset, const gfxRGBA& c) { if (mPattern) { mStops = nullptr; - if (gfxPlatform::GetCMSMode() == eCMSMode_All) { + const gfxColorManagement& colorManagement = gfxColorManagement::Instance(); + if (colorManagement.GetMode() == eCMSMode_All) { gfxRGBA cms; - qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); - if (transform) - gfxPlatform::TransformPixel(c, cms, transform); + qcms_transform *transform = colorManagement.GetRGBTransform(); + if (transform) { + colorManagement.TransformPixel(c, cms, transform); + } // Use the original alpha to avoid unnecessary float->byte->float // conversion errors diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index c2d8c05a30b4..13ad9da8098f 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -81,6 +81,8 @@ #include "nsIGfxInfo.h" +#include "gfxColorManagement.h" + using namespace mozilla; using namespace mozilla::layers; @@ -89,21 +91,6 @@ static bool gEverInitialized = false; static Mutex* gGfxPlatformPrefsLock = nullptr; -// These two may point to the same profile -static qcms_profile *gCMSOutputProfile = nullptr; -static qcms_profile *gCMSsRGBProfile = nullptr; - -static qcms_transform *gCMSRGBTransform = nullptr; -static qcms_transform *gCMSInverseRGBTransform = nullptr; -static qcms_transform *gCMSRGBATransform = nullptr; - -static bool gCMSInitialized = false; -static eCMSMode gCMSMode = eCMSMode_Off; -static int gCMSIntent = -2; - -static void ShutdownCMS(); -static void MigratePrefs(); - static bool sDrawFrameCounter = false; #include "mozilla/gfx/2D.h" @@ -118,30 +105,6 @@ static PRLogModuleInfo *sTextrunuiLog = nullptr; static PRLogModuleInfo *sCmapDataLog = nullptr; #endif -/* Class to listen for pref changes so that chrome code can dynamically - force sRGB as an output profile. See Bug #452125. */ -class SRGBOverrideObserver MOZ_FINAL : public nsIObserver, - public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER -}; - -NS_IMPL_ISUPPORTS2(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference) - -NS_IMETHODIMP -SRGBOverrideObserver::Observe(nsISupports *aSubject, - const char *aTopic, - const PRUnichar *someData) -{ - NS_ASSERTION(NS_strcmp(someData, - NS_LITERAL_STRING("gfx.color_mangement.force_srgb").get()), - "Restarting CMS on wrong pref!"); - ShutdownCMS(); - return NS_OK; -} - #define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled" #define GFX_PREF_HARFBUZZ_SCRIPTS "gfx.font_rendering.harfbuzz.scripts" @@ -397,12 +360,8 @@ gfxPlatform::Init() NS_RUNTIMEABORT("Could not initialize gfxFontCache"); } - /* Pref migration hook. */ - MigratePrefs(); - - /* Create and register our CMS Override observer. */ - gPlatform->mSRGBOverrideObserver = new SRGBOverrideObserver(); - Preferences::AddWeakObserver(gPlatform->mSRGBOverrideObserver, "gfx.color_management.force_srgb"); + // Initialize the preferences, and set the fallback profile + gfxColorManagement::InstanceNC().Initialize(GetPlatform()->GetPlatformCMSOutputProfile()); gPlatform->mFontPrefsObserver = new FontPrefsObserver(); Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs); @@ -434,8 +393,6 @@ gfxPlatform::Init() mozilla::Preferences::AddBoolVarCache(&sDrawFrameCounter, "layers.frame-counter", false); - - CreateCMSOutputProfile(); } void @@ -451,16 +408,11 @@ gfxPlatform::Shutdown() #endif // Free the various non-null transforms and loaded profiles - ShutdownCMS(); + gfxColorManagement::Destroy(); // In some cases, gPlatform may not be created but Shutdown() called, // e.g., during xpcshell tests. if (gPlatform) { - /* Unregister our CMS Override callback. */ - NS_ASSERTION(gPlatform->mSRGBOverrideObserver, "mSRGBOverrideObserver has alreay gone"); - Preferences::RemoveObserver(gPlatform->mSRGBOverrideObserver, "gfx.color_management.force_srgb"); - gPlatform->mSRGBOverrideObserver = nullptr; - NS_ASSERTION(gPlatform->mFontPrefsObserver, "mFontPrefsObserver has alreay gone"); Preferences::RemoveObservers(gPlatform->mFontPrefsObserver, kObservedPrefs); gPlatform->mFontPrefsObserver = nullptr; @@ -1486,255 +1438,12 @@ gfxPlatform::OffMainThreadCompositingEnabled() CompositorChild::ChildProcessHasCompositor(); } -eCMSMode -gfxPlatform::GetCMSMode() -{ - if (gCMSInitialized == false) { - gCMSInitialized = true; - nsresult rv; - - int32_t mode; - rv = Preferences::GetInt("gfx.color_management.mode", &mode); - if (NS_SUCCEEDED(rv) && (mode >= 0) && (mode < eCMSMode_AllCount)) { - gCMSMode = static_cast(mode); - } - - bool enableV4; - rv = Preferences::GetBool("gfx.color_management.enablev4", &enableV4); - if (NS_SUCCEEDED(rv) && enableV4) { - qcms_enable_iccv4(); - } - } - return gCMSMode; -} - -int -gfxPlatform::GetRenderingIntent() -{ - if (gCMSIntent == -2) { - - /* Try to query the pref system for a rendering intent. */ - int32_t pIntent; - if (NS_SUCCEEDED(Preferences::GetInt("gfx.color_management.rendering_intent", &pIntent))) { - /* If the pref is within range, use it as an override. */ - if ((pIntent >= QCMS_INTENT_MIN) && (pIntent <= QCMS_INTENT_MAX)) { - gCMSIntent = pIntent; - } - /* If the pref is out of range, use embedded profile. */ - else { - gCMSIntent = -1; - } - } - /* If we didn't get a valid intent from prefs, use the default. */ - else { - gCMSIntent = QCMS_INTENT_DEFAULT; - } - } - return gCMSIntent; -} - -void -gfxPlatform::TransformPixel(const gfxRGBA& in, gfxRGBA& out, qcms_transform *transform) -{ - - if (transform) { - /* we want the bytes in RGB order */ -#ifdef IS_LITTLE_ENDIAN - /* ABGR puts the bytes in |RGBA| order on little endian */ - uint32_t packed = in.Packed(gfxRGBA::PACKED_ABGR); - qcms_transform_data(transform, - (uint8_t *)&packed, (uint8_t *)&packed, - 1); - out.~gfxRGBA(); - new (&out) gfxRGBA(packed, gfxRGBA::PACKED_ABGR); -#else - /* ARGB puts the bytes in |ARGB| order on big endian */ - uint32_t packed = in.Packed(gfxRGBA::PACKED_ARGB); - /* add one to move past the alpha byte */ - qcms_transform_data(transform, - (uint8_t *)&packed + 1, (uint8_t *)&packed + 1, - 1); - out.~gfxRGBA(); - new (&out) gfxRGBA(packed, gfxRGBA::PACKED_ARGB); -#endif - } - - else if (&out != &in) - out = in; -} - qcms_profile * gfxPlatform::GetPlatformCMSOutputProfile() { return nullptr; } -void -gfxPlatform::CreateCMSOutputProfile() -{ - if (!gCMSOutputProfile) { - /* Determine if we're using the internal override to force sRGB as - an output profile for reftests. See Bug 452125. - - Note that we don't normally (outside of tests) set a - default value of this preference, which means nsIPrefBranch::GetBoolPref - will typically throw (and leave its out-param untouched). - */ - if (Preferences::GetBool("gfx.color_management.force_srgb", false)) { - gCMSOutputProfile = GetCMSsRGBProfile(); - } - - if (!gCMSOutputProfile) { - nsAdoptingCString fname = Preferences::GetCString("gfx.color_management.display_profile"); - if (!fname.IsEmpty()) { - gCMSOutputProfile = qcms_profile_from_path(fname); - } - } - - if (!gCMSOutputProfile) { - gCMSOutputProfile = - gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfile(); - } - - /* Determine if the profile looks bogus. If so, close the profile - * and use sRGB instead. See bug 460629, */ - if (gCMSOutputProfile && qcms_profile_is_bogus(gCMSOutputProfile)) { - NS_ASSERTION(gCMSOutputProfile != GetCMSsRGBProfile(), - "Builtin sRGB profile tagged as bogus!!!"); - qcms_profile_release(gCMSOutputProfile); - gCMSOutputProfile = nullptr; - } - - if (!gCMSOutputProfile) { - gCMSOutputProfile = GetCMSsRGBProfile(); - } - /* Precache the LUT16 Interpolations for the output profile. See - bug 444661 for details. */ - qcms_profile_precache_output_transform(gCMSOutputProfile); - } -} - -qcms_profile * -gfxPlatform::GetCMSOutputProfile() -{ - return gCMSOutputProfile; -} - -qcms_profile * -gfxPlatform::GetCMSsRGBProfile() -{ - if (!gCMSsRGBProfile) { - - /* Create the profile using qcms. */ - gCMSsRGBProfile = qcms_profile_sRGB(); - } - return gCMSsRGBProfile; -} - -qcms_transform * -gfxPlatform::GetCMSRGBTransform() -{ - if (!gCMSRGBTransform) { - qcms_profile *inProfile, *outProfile; - outProfile = GetCMSOutputProfile(); - inProfile = GetCMSsRGBProfile(); - - if (!inProfile || !outProfile) - return nullptr; - - gCMSRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8, - outProfile, QCMS_DATA_RGB_8, - QCMS_INTENT_PERCEPTUAL); - } - - return gCMSRGBTransform; -} - -qcms_transform * -gfxPlatform::GetCMSInverseRGBTransform() -{ - if (!gCMSInverseRGBTransform) { - qcms_profile *inProfile, *outProfile; - inProfile = GetCMSOutputProfile(); - outProfile = GetCMSsRGBProfile(); - - if (!inProfile || !outProfile) - return nullptr; - - gCMSInverseRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8, - outProfile, QCMS_DATA_RGB_8, - QCMS_INTENT_PERCEPTUAL); - } - - return gCMSInverseRGBTransform; -} - -qcms_transform * -gfxPlatform::GetCMSRGBATransform() -{ - if (!gCMSRGBATransform) { - qcms_profile *inProfile, *outProfile; - outProfile = GetCMSOutputProfile(); - inProfile = GetCMSsRGBProfile(); - - if (!inProfile || !outProfile) - return nullptr; - - gCMSRGBATransform = qcms_transform_create(inProfile, QCMS_DATA_RGBA_8, - outProfile, QCMS_DATA_RGBA_8, - QCMS_INTENT_PERCEPTUAL); - } - - return gCMSRGBATransform; -} - -/* Shuts down various transforms and profiles for CMS. */ -static void ShutdownCMS() -{ - - if (gCMSRGBTransform) { - qcms_transform_release(gCMSRGBTransform); - gCMSRGBTransform = nullptr; - } - if (gCMSInverseRGBTransform) { - qcms_transform_release(gCMSInverseRGBTransform); - gCMSInverseRGBTransform = nullptr; - } - if (gCMSRGBATransform) { - qcms_transform_release(gCMSRGBATransform); - gCMSRGBATransform = nullptr; - } - if (gCMSOutputProfile) { - qcms_profile_release(gCMSOutputProfile); - - // handle the aliased case - if (gCMSsRGBProfile == gCMSOutputProfile) - gCMSsRGBProfile = nullptr; - gCMSOutputProfile = nullptr; - } - if (gCMSsRGBProfile) { - qcms_profile_release(gCMSsRGBProfile); - gCMSsRGBProfile = nullptr; - } - - // Reset the state variables - gCMSIntent = -2; - gCMSMode = eCMSMode_Off; - gCMSInitialized = false; -} - -static void MigratePrefs() -{ - /* Migrate from the boolean color_management.enabled pref - we now use - color_management.mode. */ - if (Preferences::HasUserValue("gfx.color_management.enabled")) { - if (Preferences::GetBool("gfx.color_management.enabled", false)) { - Preferences::SetInt("gfx.color_management.mode", static_cast(eCMSMode_All)); - } - Preferences::ClearUser("gfx.color_management.enabled"); - } -} - // default SetupClusterBoundaries, based on Unicode properties; // platform subclasses may override if they wish void diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 815850299a2e..d3e54d86f7f1 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -93,13 +93,6 @@ enum eFontPrefLang { eFontPrefLang_AllCount = 33 }; -enum eCMSMode { - eCMSMode_Off = 0, // No color management - eCMSMode_All = 1, // Color manage everything - eCMSMode_TaggedOnly = 2, // Color manage tagged Images Only - eCMSMode_AllCount = 3 -}; - enum eGfxLog { // all font enumerations, localized names, fullname/psnames, cmap loads eGfxLog_fontlist = 0, @@ -501,55 +494,6 @@ public: static bool ComponentAlphaEnabled(); - /** - * Are we going to try color management? - */ - static eCMSMode GetCMSMode(); - - /** - * Determines the rendering intent for color management. - * - * If the value in the pref gfx.color_management.rendering_intent is a - * valid rendering intent as defined in gfx/qcms/qcms.h, that - * value is returned. Otherwise, -1 is returned and the embedded intent - * should be used. - * - * See bug 444014 for details. - */ - static int GetRenderingIntent(); - - /** - * Convert a pixel using a cms transform in an endian-aware manner. - * - * Sets 'out' to 'in' if transform is nullptr. - */ - static void TransformPixel(const gfxRGBA& in, gfxRGBA& out, qcms_transform *transform); - - /** - * Return the output device ICC profile. - */ - static qcms_profile* GetCMSOutputProfile(); - - /** - * Return the sRGB ICC profile. - */ - static qcms_profile* GetCMSsRGBProfile(); - - /** - * Return sRGB -> output device transform. - */ - static qcms_transform* GetCMSRGBTransform(); - - /** - * Return output -> sRGB device transform. - */ - static qcms_transform* GetCMSInverseRGBTransform(); - - /** - * Return sRGBA -> output device transform. - */ - static qcms_transform* GetCMSRGBATransform(); - virtual void FontsPrefsChanged(const char *aPref); void OrientationSyncPrefsObserverChanged(); @@ -678,8 +622,6 @@ private: */ static void Init(); - static void CreateCMSOutputProfile(); - friend int RecordingPrefChanged(const char *aPrefName, void *aClosure); virtual qcms_profile* GetPlatformCMSOutputProfile(); @@ -688,7 +630,6 @@ private: nsRefPtr mScreenReferenceSurface; nsTArray mCJKPrefLangs; - nsCOMPtr mSRGBOverrideObserver; nsCOMPtr mFontPrefsObserver; nsCOMPtr mOrientationSyncPrefsObserver; diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index d3673b4676b5..8e8b091bdcfd 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -16,6 +16,7 @@ EXPORTS += [ 'gfxBlur.h', 'gfxCachedTempSurface.h', 'gfxColor.h', + 'gfxColorManagement.h', 'gfxContext.h', 'gfxDrawable.h', 'gfxFailure.h', @@ -236,6 +237,7 @@ CPP_SOURCES += [ 'gfxBaseSharedMemorySurface.cpp', 'gfxBlur.cpp', 'gfxCachedTempSurface.cpp', + 'gfxColorManagement.cpp', 'gfxContext.cpp', 'gfxDrawable.cpp', 'gfxFont.cpp', diff --git a/image/decoders/nsGIFDecoder2.cpp b/image/decoders/nsGIFDecoder2.cpp index 269837c02b13..53815e30f36f 100644 --- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -45,7 +45,7 @@ mailing address. #include "RasterImage.h" #include "gfxColor.h" -#include "gfxPlatform.h" +#include "gfxColorManagement.h" #include "qcms.h" #include @@ -504,8 +504,8 @@ nsGIFDecoder2::DoLzw(const uint8_t *q) static void ConvertColormap(uint32_t *aColormap, uint32_t aColors) { // Apply CMS transformation if enabled and available - if (gfxPlatform::GetCMSMode() == eCMSMode_All) { - qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); + if (gfxColorManagement::Instance().GetMode() == eCMSMode_All) { + qcms_transform *transform = gfxColorManagement::Instance().GetRGBTransform(); if (transform) qcms_transform_data(transform, aColormap, aColormap, aColors); } diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index 58c653fe3ca7..db6c254f0fe6 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -14,11 +14,10 @@ #include "nspr.h" #include "nsCRT.h" #include "gfxColor.h" +#include "gfxColorManagement.h" #include "jerror.h" -#include "gfxPlatform.h" - extern "C" { #include "iccjpeg.h" } @@ -137,7 +136,7 @@ nsJPEGDecoder::SpeedHistogram() void nsJPEGDecoder::InitInternal() { - mCMSMode = gfxPlatform::GetCMSMode(); + mCMSMode = gfxColorManagement::Instance().GetMode(); if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) mCMSMode = eCMSMode_Off; @@ -314,17 +313,18 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount) type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); #endif - if (gfxPlatform::GetCMSOutputProfile()) { + const gfxColorManagement& colorManagement = gfxColorManagement::Instance(); + if (colorManagement.GetOutputProfile()) { /* Calculate rendering intent. */ - int intent = gfxPlatform::GetRenderingIntent(); + int intent = colorManagement.GetRenderingIntent(); if (intent == -1) intent = qcms_profile_get_rendering_intent(mInProfile); /* Create the color management transform. */ mTransform = qcms_transform_create(mInProfile, type, - gfxPlatform::GetCMSOutputProfile(), + colorManagement.GetOutputProfile(), QCMS_DATA_RGB_8, (qcms_intent)intent); } @@ -621,7 +621,7 @@ nsJPEGDecoder::OutputScanlines(bool* suspend) } if (mCMSMode == eCMSMode_All) { /* No embedded ICC profile - treat as sRGB */ - qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); + qcms_transform *transform = gfxColorManagement::Instance().GetRGBTransform(); if (transform) { qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width); } diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index a89880d67b71..782459b850d3 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -15,12 +15,12 @@ #include "RasterImage.h" #include "gfxColor.h" +#include "gfxColorManagement.h" #include "nsColor.h" #include "nspr.h" #include "png.h" -#include "gfxPlatform.h" #include namespace mozilla { @@ -207,7 +207,7 @@ nsPNGDecoder::InitInternal() return; } - mCMSMode = gfxPlatform::GetCMSMode(); + mCMSMode = gfxColorManagement::Instance().GetMode(); if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) mCMSMode = eCMSMode_Off; mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA) != 0; @@ -553,15 +553,16 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) qcms_data_type inType = QCMS_DATA_RGBA_8; uint32_t intent = -1; uint32_t pIntent; + const gfxColorManagement& colorManagement = gfxColorManagement::Instance(); if (decoder->mCMSMode != eCMSMode_Off) { - intent = gfxPlatform::GetRenderingIntent(); + intent = colorManagement.GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent); /* If we're not mandating an intent, use the one from the image. */ if (intent == uint32_t(-1)) intent = pIntent; } - if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { + if (decoder->mInProfile && colorManagement.GetOutputProfile()) { qcms_data_type outType; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) @@ -571,7 +572,7 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, - gfxPlatform::GetCMSOutputProfile(), + colorManagement.GetOutputProfile(), outType, (qcms_intent)intent); } else { @@ -583,9 +584,9 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) if (decoder->mCMSMode == eCMSMode_All) { if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) - decoder->mTransform = gfxPlatform::GetCMSRGBATransform(); + decoder->mTransform = colorManagement.GetRGBATransform(); else - decoder->mTransform = gfxPlatform::GetCMSRGBTransform(); + decoder->mTransform = colorManagement.GetRGBTransform(); } } diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 61c2946e9d35..85ceb4ba6b1a 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -33,7 +33,7 @@ #include "nsIWidgetListener.h" #include "nsIPresShell.h" -#include "gfxPlatform.h" +#include "gfxColorManagement.h" #include "qcms.h" #include "mozilla/Preferences.h" @@ -1989,8 +1989,8 @@ NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, bool aActive // Transform from sRGBA to monitor RGBA. This seems like it would make trying // to match the system appearance lame, so probably we just shouldn't color // correct chrome. - if (gfxPlatform::GetCMSMode() == eCMSMode_All) { - qcms_transform *transform = gfxPlatform::GetCMSRGBATransform(); + if (gfxColorManagement::Instance().GetMode() == eCMSMode_All) { + qcms_transform *transform = gfxColorManagement::Instance().GetRGBATransform(); if (transform) { uint8_t color[3]; color[0] = NS_GET_R(aColor); diff --git a/widget/xpwidgets/nsXPLookAndFeel.cpp b/widget/xpwidgets/nsXPLookAndFeel.cpp index bd5fe687c185..ead4f30b526b 100644 --- a/widget/xpwidgets/nsXPLookAndFeel.cpp +++ b/widget/xpwidgets/nsXPLookAndFeel.cpp @@ -13,7 +13,7 @@ #include "nsFont.h" #include "mozilla/Preferences.h" -#include "gfxPlatform.h" +#include "gfxColorManagement.h" #include "qcms.h" #ifdef DEBUG @@ -616,9 +616,9 @@ nsXPLookAndFeel::GetColorImpl(ColorID aID, nscolor &aResult) } if (sUseNativeColors && NS_SUCCEEDED(NativeGetColor(aID, aResult))) { - if ((gfxPlatform::GetCMSMode() == eCMSMode_All) && + if ((gfxColorManagement::Instance().GetMode() == eCMSMode_All) && !IsSpecialColor(aID, aResult)) { - qcms_transform *transform = gfxPlatform::GetCMSInverseRGBTransform(); + qcms_transform *transform = gfxColorManagement::Instance().GetInverseRGBTransform(); if (transform) { uint8_t color[3]; color[0] = NS_GET_R(aResult);