Force cms to use sRGB as an output profile during reftests - bug 452125.r=vlad
authorBobby Holley <bholley@mozilla.com>
Mon, 08 Sep 2008 14:47:26 -0700
changeset 19003 4b2977a03aba9d9ca4b719cdc7f95c9441a7da6f
parent 19002 7068d034c12b2c2c5df8c3bc3c0d2d10f80b3e1e
child 19004 ce0b8bf8621c66606bb46120773b2fc6036610bb
push id1898
push userbobbyholley@stanford.edu
push dateMon, 08 Sep 2008 21:47:52 +0000
treeherdermozilla-central@4b2977a03aba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad
bugs452125
milestone1.9.1b1pre
Force cms to use sRGB as an output profile during reftests - bug 452125.r=vlad
gfx/thebes/public/gfxPlatform.h
gfx/thebes/src/gfxPlatform.cpp
layout/tools/reftest/reftest-cmdline.js
layout/tools/reftest/reftest.js
modules/lcms/src/cmsxform.c
--- a/gfx/thebes/public/gfxPlatform.h
+++ b/gfx/thebes/public/gfxPlatform.h
@@ -37,16 +37,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_PLATFORM_H
 #define GFX_PLATFORM_H
 
 #include "prtypes.h"
 #include "nsVoidArray.h"
 
+#include "nsIObserver.h"
+
 #include "gfxTypes.h"
 #include "gfxASurface.h"
 
 #ifdef XP_OS2
 #undef OS2EMX_PLAIN_CHAR
 #endif
 
 typedef void* cmsHPROFILE;
@@ -261,11 +263,13 @@ public:
     static cmsHTRANSFORM GetCMSRGBATransform();
 
 protected:
     gfxPlatform() { }
     virtual ~gfxPlatform();
 
 private:
     virtual cmsHPROFILE GetPlatformCMSOutputProfile();
+
+    nsCOMPtr<nsIObserver> overrideObserver;
 };
 
 #endif /* GFX_PLATFORM_H */
--- a/gfx/thebes/src/gfxPlatform.cpp
+++ b/gfx/thebes/src/gfxPlatform.cpp
@@ -55,43 +55,79 @@
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxTextRunCache.h"
 #include "gfxTextRunWordCache.h"
 
 #include "nsIPref.h"
 #include "nsServiceManagerUtils.h"
 
+#include "nsWeakReference.h"
+
 #ifdef MOZ_ENABLE_GLITZ
 #include <stdlib.h>
 #endif
 
 #include "cairo.h"
 #include "lcms.h"
 
 #include "plstr.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
+#include "nsIPrefBranch2.h"
 
 gfxPlatform *gPlatform = nsnull;
 int gGlitzState = -1;
 
 // These two may point to the same profile
 static cmsHPROFILE gCMSOutputProfile = nsnull;
 static cmsHPROFILE gCMSsRGBProfile = nsnull;
 
 static cmsHTRANSFORM gCMSRGBTransform = nsnull;
 static cmsHTRANSFORM gCMSInverseRGBTransform = nsnull;
 static cmsHTRANSFORM gCMSRGBATransform = nsnull;
 
+static PRBool gCMSInitialized = PR_FALSE;
+static eCMSMode gCMSMode = eCMSMode_Off;
+static int gCMSIntent = -2;
+
 static const char *CMPrefName = "gfx.color_management.mode";
 static const char *CMPrefNameOld = "gfx.color_management.enabled";
 static const char *CMIntentPrefName = "gfx.color_management.rendering_intent";
+static const char *CMProfilePrefName = "gfx.color_management.display_profile";
+static const char *CMForceSRGBPrefName = "gfx.color_management.force_srgb";
+
+static void ShutdownCMS();
 static void MigratePrefs();
 
+/* Class to listen for pref changes so that chrome code can dynamically
+   force sRGB as an output profile. See Bug #452125. */
+class SRGBOverrideObserver : 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;
+}
+
+
 // this needs to match the list of pref font.default.xx entries listed in all.js!
 // the order *must* match the order in eFontPrefLang
 static const char *gPrefLangNames[] = {
     "x-western",
     "x-central-euro",
     "ja",
     "zh-TW",
     "zh-CN",
@@ -180,56 +216,44 @@ gfxPlatform::Init()
         NS_ERROR("Could not initialize gfxTextRunCache");
         Shutdown();
         return rv;
     }
 
     /* Pref migration hook. */
     MigratePrefs();
 
+    /* Create and register our CMS Override observer. */
+    gPlatform->overrideObserver = new SRGBOverrideObserver();
+    nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (prefs)
+        prefs->AddObserver(CMForceSRGBPrefName, gPlatform->overrideObserver, PR_TRUE);
+
     return NS_OK;
 }
 
 void
 gfxPlatform::Shutdown()
 {
     // These may be called before the corresponding subsystems have actually
     // started up. That's OK, they can handle it.
     gfxTextRunCache::Shutdown();
     gfxTextRunWordCache::Shutdown();
     gfxFontCache::Shutdown();
 #if defined(XP_MACOSX)
     gfxQuartzFontCache::Shutdown();
 #endif
 
     // Free the various non-null transforms and loaded profiles
-    if (gCMSRGBTransform) {
-        cmsDeleteTransform(gCMSRGBTransform);
-        gCMSRGBTransform = nsnull;
-    }
-    if (gCMSInverseRGBTransform) {
-        cmsDeleteTransform(gCMSInverseRGBTransform);
-        gCMSInverseRGBTransform = nsnull;
-    }
-    if (gCMSRGBATransform) {
-        cmsDeleteTransform(gCMSRGBATransform);
-        gCMSRGBATransform = nsnull;
-    }
-    if (gCMSOutputProfile) {
-        cmsCloseProfile(gCMSOutputProfile);
+    ShutdownCMS();
 
-        // handle the aliased case
-        if (gCMSsRGBProfile == gCMSOutputProfile)
-            gCMSsRGBProfile = nsnull;
-        gCMSOutputProfile = nsnull;
-    }
-    if (gCMSsRGBProfile) {
-        cmsCloseProfile(gCMSsRGBProfile);
-        gCMSsRGBProfile = nsnull;
-    }
+    /* Unregister our CMS Override callback. */
+    nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (prefs)
+        prefs->RemoveObserver(CMForceSRGBPrefName, gPlatform->overrideObserver);
     
     delete gPlatform;
     gPlatform = nsnull;
 }
 
 gfxPlatform::~gfxPlatform()
 {
     // The cairo folks think we should only clean up in debug builds,
@@ -460,69 +484,63 @@ gfxPlatform::AppendPrefLang(eFontPrefLan
         aPrefLangs[aLen] = aAddLang;
         aLen++;
     }
 }
 
 eCMSMode
 gfxPlatform::GetCMSMode()
 {
-    static eCMSMode sMode = eCMSMode_Off;
-    static PRBool initialized = PR_FALSE;
-
-    if (initialized == PR_FALSE) {
-        initialized = PR_TRUE;
+    if (gCMSInitialized == PR_FALSE) {
+        gCMSInitialized = PR_TRUE;
         nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
         if (prefs) {
             PRInt32 mode;
             nsresult rv =
                 prefs->GetIntPref(CMPrefName, &mode);
             if (NS_SUCCEEDED(rv) && (mode >= 0) && (mode < eCMSMode_AllCount)) {
-                sMode = static_cast<eCMSMode>(mode);
+                gCMSMode = static_cast<eCMSMode>(mode);
             }
         }
     }
-    return sMode;
+    return gCMSMode;
 }
 
 /* Chris Murphy (CM consultant) suggests this as a default in the event that we
 cannot reproduce relative + Black Point Compensation.  BPC brings an
 unacceptable performance overhead, so we go with perceptual. */
 #define INTENT_DEFAULT INTENT_PERCEPTUAL
 
 PRBool
 gfxPlatform::GetRenderingIntent()
 {
-    /* -2 means that we haven't tried querying the pref service yet. */
-    static int sIntent = -2;
-
-    if (sIntent == -2) {
+    if (gCMSIntent == -2) {
 
         /* Try to query the pref system for a rendering intent. */
         nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
         if (prefs) {
             PRInt32 pIntent;
             nsresult rv = prefs->GetIntPref(CMIntentPrefName, &pIntent);
             if (NS_SUCCEEDED(rv)) {
               
                 /* If the pref is within range, use it as an override. */
                 if ((pIntent >= INTENT_MIN) && (pIntent <= INTENT_MAX))
-                    sIntent = pIntent;
+                    gCMSIntent = pIntent;
 
                 /* If the pref is out of range, use embedded profile. */
                 else
-                    sIntent = -1;
+                    gCMSIntent = -1;
             }
         }
 
         /* If we didn't get a valid intent from prefs, use the default. */
-        if (sIntent == -2) 
-            sIntent = INTENT_DEFAULT;
+        if (gCMSIntent == -2) 
+            gCMSIntent = INTENT_DEFAULT;
     }
-    return sIntent;
+    return gCMSIntent;
 }
 
 
 cmsHPROFILE
 gfxPlatform::GetPlatformCMSOutputProfile()
 {
     return nsnull;
 }
@@ -535,28 +553,41 @@ gfxPlatform::GetCMSOutputProfile()
 #ifdef DEBUG_tor
         cmsErrorAction(LCMS_ERROR_SHOW);
 #else
         cmsErrorAction(LCMS_ERROR_IGNORE);
 #endif
 
         nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
         if (prefs) {
-            nsXPIDLCString fname;
-            nsresult rv =
-                prefs->GetCharPref("gfx.color_management.display_profile",
-                                   getter_Copies(fname));
-            if (NS_SUCCEEDED(rv) && !fname.IsEmpty()) {
-                gCMSOutputProfile = cmsOpenProfileFromFile(fname, "r");
-#ifdef DEBUG_tor
-                if (gCMSOutputProfile)
-                    fprintf(stderr,
-                            "ICM profile read from %s successfully\n",
-                            fname.get());
-#endif
+
+            nsresult rv;
+
+            /* Determine if we're using the internal override to force sRGB as
+               an output profile for reftests. See Bug 452125. */
+            PRBool hasSRGBOverride, doSRGBOverride;
+            rv = prefs->PrefHasUserValue(CMForceSRGBPrefName, &hasSRGBOverride);
+            if (NS_SUCCEEDED(rv) && hasSRGBOverride) {
+                rv = prefs->GetBoolPref(CMForceSRGBPrefName, &doSRGBOverride);
+                if (NS_SUCCEEDED(rv) && doSRGBOverride)
+                    gCMSOutputProfile = GetCMSsRGBProfile();
+            }
+
+            if (!gCMSOutputProfile) {
+
+                nsXPIDLCString fname;
+                rv = prefs->GetCharPref(CMProfilePrefName,
+                                        getter_Copies(fname));
+                if (NS_SUCCEEDED(rv) && !fname.IsEmpty()) {
+                    gCMSOutputProfile = cmsOpenProfileFromFile(fname, "r");
+                    if (gCMSOutputProfile)
+                        fprintf(stderr,
+                                "ICM profile read from %s successfully\n",
+                                fname.get());
+                }
             }
         }
 
         if (!gCMSOutputProfile) {
             gCMSOutputProfile =
                 gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfile();
         }
 
@@ -639,16 +670,51 @@ gfxPlatform::GetCMSRGBATransform()
         gCMSRGBATransform = cmsCreateTransform(inProfile, TYPE_RGBA_8,
                                                outProfile, TYPE_RGBA_8,
                                                INTENT_PERCEPTUAL, cmsFLAGS_FLOATSHAPER);
     }
 
     return gCMSRGBATransform;
 }
 
+/* Shuts down various transforms and profiles for CMS. */
+static void ShutdownCMS()
+{
+
+    if (gCMSRGBTransform) {
+        cmsDeleteTransform(gCMSRGBTransform);
+        gCMSRGBTransform = nsnull;
+    }
+    if (gCMSInverseRGBTransform) {
+        cmsDeleteTransform(gCMSInverseRGBTransform);
+        gCMSInverseRGBTransform = nsnull;
+    }
+    if (gCMSRGBATransform) {
+        cmsDeleteTransform(gCMSRGBATransform);
+        gCMSRGBATransform = nsnull;
+    }
+    if (gCMSOutputProfile) {
+        cmsCloseProfile(gCMSOutputProfile);
+
+        // handle the aliased case
+        if (gCMSsRGBProfile == gCMSOutputProfile)
+            gCMSsRGBProfile = nsnull;
+        gCMSOutputProfile = nsnull;
+    }
+    if (gCMSsRGBProfile) {
+        cmsCloseProfile(gCMSsRGBProfile);
+        gCMSsRGBProfile = nsnull;
+    }
+
+    // Reset the state variables
+    gCMSIntent = -2;
+    gCMSMode = eCMSMode_Off;
+    gCMSInitialized = PR_FALSE;
+}
+
 static void MigratePrefs()
 {
 
     /* Load the pref service. If we don't get it die quietly since this isn't
        critical code. */
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (!prefs)
         return;
--- a/layout/tools/reftest/reftest-cmdline.js
+++ b/layout/tools/reftest/reftest-cmdline.js
@@ -92,16 +92,22 @@ RefTestCmdLineHandler.prototype =
       catch (e) {
         return;
       }
     }
     catch (e) {
       cmdLine.handleFlag("reftest", true);
     }
 
+    /* Force sRGB as an output profile for color management before we load a
+       window. */
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+                getService(Components.interfaces.nsIPrefBranch2);
+    prefs.setBoolPref("gfx.color_management.force_srgb", true);
+
     var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                            .getService(nsIWindowWatcher);
     wwatch.openWindow(null, "chrome://reftest/content/reftest.xul", "_blank",
                       "chrome,dialog=no,all", args);
     cmdLine.preventDefault = true;
   },
 
   helpInfo : "  -reftest <file>    Run layout acceptance tests on given manifest.\n"
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -116,16 +116,21 @@ function OnRefTestLoad()
         //gBrowser.loadURI('data:text/plain,' + ex);
         dump("REFTEST TEST-FAIL | | EXCEPTION: " + ex + "\n");
         DoneTests();
     }
 }
 
 function OnRefTestUnload()
 {
+    /* Clear the sRGB forcing pref to leave the profile as we found it. */
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+                getService(Components.interfaces.nsIPrefBranch2);
+    prefs.clearUserPref("gfx.color_management.force_srgb");
+
     gBrowser.removeEventListener("load", OnDocumentLoad, true);
 }
 
 function ReadTopManifest(aFileURL)
 {
     gURLs = new Array();
     var url = gIOService.newURI(aFileURL, null, null);
     if (!url || !url.schemeIs("file"))
--- a/modules/lcms/src/cmsxform.c
+++ b/modules/lcms/src/cmsxform.c
@@ -249,16 +249,32 @@ void NullXFORM(_LPcmsTRANSFORM p,
        WORD wIn[MAXCHANNELS];
        register unsigned int i, n;
 
 
        accum  = (LPBYTE) in;
        output = (LPBYTE) out;
        n = Size;                    // Buffer len
 
+       // If the input and output formats are the same,
+       // we don't need to pack and unpack pixels
+       if (p -> InputFormat == p -> OutputFormat) {
+
+              // Only copy bytes if the buffers aren't the same
+              if (in != out) {
+
+                     // Copy in a nondestructive manner in case
+                     // the buffers overlap for some reason
+                     memmove(out, in, 
+                             Size * T_BYTES(p -> InputFormat) * T_CHANNELS(p -> InputFormat));
+              }
+
+              return;
+       }
+
        for (i=0; i < n; i++)
        {
        accum = p -> FromInput(p, wIn, accum);
        output = p -> ToOutput(p, wIn, output);
        }
 
 }
 
@@ -1518,16 +1534,22 @@ static
                    if ((*FromTagPtr == 0) && 
                        (*ToTagPtr == 0) && 
                        (!p->PreviewProfile) && 
                        (p -> Intent != INTENT_ABSOLUTE_COLORIMETRIC) && 
                        (p -> EntryColorSpace == icSigRgbData) && 
                        (p -> ExitColorSpace == icSigRgbData)  &&
                        !(p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)) {
 
+                          // If the input profile pointer-matches with the output profile, 
+                          // optimize the transformation away into a null xform
+                          if (p -> InputProfile == p -> OutputProfile) {
+                                 p -> xform = NullXFORM;
+                                 return p;
+                          }
 
                           // If the floating point path is requested, see if we support it
                           if (p -> dwOriginalFlags & cmsFLAGS_FLOATSHAPER)
                           {
 
 #ifndef HAVE_SSE2_INTEL_MNEMONICS 
                                  // Turn it off if we can't compile it
                                  p -> dwOriginalFlags &= ~cmsFLAGS_FLOATSHAPER;