mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
adding color management capabilities -- preffed off. bug 16769. patch from tor. r=bsmedberg, sr=me
This commit is contained in:
parent
2db409b949
commit
d04ad3b03b
@ -176,6 +176,14 @@ gfx/cairo/glitz/src/wgl/Makefile
|
||||
"
|
||||
fi
|
||||
|
||||
if [ !"$MOZ_NATIVE_LCMS" ] ; then
|
||||
MAKEFILES_gfx="$MAKEFILES_gfx
|
||||
modules/lcms/Makefile
|
||||
modules/lcms/include/Makefile
|
||||
modules/lcms/src/Makefile
|
||||
"
|
||||
fi
|
||||
|
||||
MAKEFILES_htmlparser="
|
||||
parser/htmlparser/Makefile
|
||||
parser/htmlparser/robot/Makefile
|
||||
|
@ -263,7 +263,7 @@ OS_LDFLAGS = @LDFLAGS@
|
||||
OS_COMPILE_CFLAGS = $(OS_CPPFLAGS) @COMPILE_CFLAGS@
|
||||
OS_COMPILE_CXXFLAGS = $(OS_CPPFLAGS) @COMPILE_CXXFLAGS@
|
||||
|
||||
OS_INCLUDES = $(NSPR_CFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(ZLIB_CFLAGS)
|
||||
OS_INCLUDES = $(NSPR_CFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(ZLIB_CFLAGS) $(LCMS_CFLAGS)
|
||||
OS_LIBS = @LIBS@
|
||||
ACDEFINES = @MOZ_DEFINES@
|
||||
|
||||
@ -406,6 +406,15 @@ PNG_LIBS = @MOZ_PNG_LIBS@
|
||||
PNG_REQUIRES = png
|
||||
endif
|
||||
|
||||
MOZ_NATIVE_LCMS = @MOZ_NATIVE_LCMS@
|
||||
LCMS_CFLAGS = @LCMS_CFLAGS@
|
||||
LCMS_LIBS = @LCMS_LIBS@
|
||||
ifdef MOZ_NATIVE_LCMS
|
||||
LCMS_REQUIRES =
|
||||
else
|
||||
LCMS_REQUIRES = lcms
|
||||
endif
|
||||
|
||||
NSPR_CONFIG = @NSPR_CONFIG@
|
||||
NSPR_CFLAGS = @NSPR_CFLAGS@
|
||||
NSPR_LIBS = @NSPR_LIBS@
|
||||
|
@ -69,6 +69,7 @@ STATIC_EXTRA_LIBS += \
|
||||
$(PNG_LIBS) \
|
||||
$(JPEG_LIBS) \
|
||||
$(ZLIB_LIBS) \
|
||||
$(LCMS_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_PSM
|
||||
|
@ -937,3 +937,6 @@ png.h
|
||||
#if MOZ_NATIVE_ZLIB==1
|
||||
zlib.h
|
||||
#endif
|
||||
#if MOZ_TREE_LCMS==1
|
||||
lcms.h
|
||||
#endif
|
||||
|
30
configure.in
30
configure.in
@ -126,6 +126,7 @@ GCONF_VERSION=1.2.1
|
||||
LIBGNOME_VERSION=2.0
|
||||
STARTUP_NOTIFICATION_VERSION=0.8
|
||||
DBUS_VERSION=0.60
|
||||
LCMS_VERSION=1.17
|
||||
|
||||
MSMANIFEST_TOOL=
|
||||
|
||||
@ -7049,6 +7050,35 @@ AC_SUBST(MOZ_TREE_CAIRO)
|
||||
AC_SUBST(MOZ_CAIRO_CFLAGS)
|
||||
AC_SUBST(MOZ_CAIRO_LIBS)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Check for lcms
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_NATIVE_LCMS=
|
||||
MOZ_ARG_ENABLE_BOOL(system-lcms,
|
||||
[ --enable-system-lcms Use system lcms (located with pkgconfig)],
|
||||
MOZ_NATIVE_LCMS=1,
|
||||
MOZ_NATIVE_LCMS= )
|
||||
|
||||
if test -z "$MOZ_NATIVE_LCMS"
|
||||
then
|
||||
LCMS_CFLAGS=
|
||||
if test "$OS_ARCH" = "WINNT"; then
|
||||
if test -z "$BUILD_STATIC_LIBS" -a -z "$MOZ_ENABLE_LIBXUL"; then
|
||||
LCMS_CFLAGS=-DLCMS_DLL
|
||||
fi
|
||||
LCMS_LIBS='$(LIBXUL_DIST)/lib/mozlcms.lib'
|
||||
else
|
||||
LCMS_LIBS='-L$(LIBXUL_DIST)/bin -lmozlcms'
|
||||
fi
|
||||
else
|
||||
PKG_CHECK_MODULES(LCMS, lcms >= $LCMS_VERSION)
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_NATIVE_LCMS)
|
||||
AC_SUBST(LCMS_CFLAGS)
|
||||
AC_SUBST(LCMS_LIBS)
|
||||
|
||||
dnl ========================================================
|
||||
dnl disable xul
|
||||
dnl ========================================================
|
||||
|
@ -45,6 +45,9 @@
|
||||
#include "gfxTypes.h"
|
||||
#include "gfxASurface.h"
|
||||
|
||||
typedef void* cmsHPROFILE;
|
||||
typedef void* cmsHTRANSFORM;
|
||||
|
||||
class gfxImageSurface;
|
||||
class gfxFontGroup;
|
||||
struct gfxFontStyle;
|
||||
@ -135,10 +138,32 @@ public:
|
||||
|
||||
void GetPrefFonts(const char *aLangGroup, nsString& array, PRBool aAppendUnicode = PR_TRUE);
|
||||
|
||||
/**
|
||||
* Are we going to try color management?
|
||||
*/
|
||||
static PRBool IsCMSEnabled();
|
||||
|
||||
/**
|
||||
* Return the output device ICC profile.
|
||||
*/
|
||||
static cmsHPROFILE GetCMSOutputProfile();
|
||||
|
||||
/**
|
||||
* Return sRGB -> output device transform.
|
||||
*/
|
||||
static cmsHTRANSFORM GetCMSRGBTransform();
|
||||
|
||||
/**
|
||||
* Return sRGBA -> output device transform.
|
||||
*/
|
||||
static cmsHTRANSFORM GetCMSRGBATransform();
|
||||
|
||||
protected:
|
||||
gfxPlatform() { }
|
||||
virtual ~gfxPlatform();
|
||||
|
||||
private:
|
||||
virtual cmsHPROFILE GetPlatformCMSOutputProfile();
|
||||
};
|
||||
|
||||
#endif /* GFX_PLATFORM_H */
|
||||
|
@ -88,6 +88,9 @@ protected:
|
||||
|
||||
static PRInt32 sDPI;
|
||||
static gfxFontconfigUtils *sFontconfigUtils;
|
||||
|
||||
private:
|
||||
virtual cmsHPROFILE GetPlatformCMSOutputProfile();
|
||||
};
|
||||
|
||||
#endif /* GFX_PLATFORM_GTK_H */
|
||||
|
@ -63,6 +63,9 @@ public:
|
||||
const nsACString& aGenericFamily,
|
||||
nsStringArray& aListOfFonts);
|
||||
nsresult UpdateFontList();
|
||||
|
||||
private:
|
||||
virtual cmsHPROFILE GetPlatformCMSOutputProfile();
|
||||
};
|
||||
|
||||
#endif /* GFX_PLATFORM_MAC_H */
|
||||
|
@ -107,6 +107,8 @@ private:
|
||||
nsRefPtr<FontEntry>& aFontEntry,
|
||||
void* userArg);
|
||||
|
||||
virtual cmsHPROFILE GetPlatformCMSOutputProfile();
|
||||
|
||||
nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFonts;
|
||||
nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFontAliases;
|
||||
nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFontSubstitutes;
|
||||
|
@ -18,6 +18,7 @@ REQUIRES = \
|
||||
pref \
|
||||
xpcom \
|
||||
unicharutil \
|
||||
$(LCMS_REQUIRES) \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
@ -44,6 +45,7 @@ EXTRA_DSO_LDOPTS += \
|
||||
$(XPCOM_LIBS) \
|
||||
$(NSPR_LIBS) \
|
||||
$(ZLIB_LIBS) \
|
||||
$(LCMS_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
@ -46,6 +46,7 @@
|
||||
#endif
|
||||
|
||||
#include "cairo.h"
|
||||
#include "lcms.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
|
||||
@ -53,7 +54,7 @@
|
||||
#include "gfxMatrix.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "gfxPattern.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
|
||||
gfxContext::gfxContext(gfxASurface *surface) :
|
||||
@ -606,6 +607,27 @@ gfxContext::GetClipExtents()
|
||||
void
|
||||
gfxContext::SetColor(const gfxRGBA& c)
|
||||
{
|
||||
if (gfxPlatform::IsCMSEnabled()) {
|
||||
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
||||
if (transform) {
|
||||
#ifdef IS_LITTLE_ENDIAN
|
||||
PRUint32 packed = c.Packed(gfxRGBA::PACKED_ABGR);
|
||||
cmsDoTransform(transform,
|
||||
(PRUint8 *)&packed, (PRUint8 *)&packed,
|
||||
1);
|
||||
gfxRGBA cms(packed, gfxRGBA::PACKED_ABGR);
|
||||
#else
|
||||
PRUint32 packed = c.Packed(gfxRGBA::PACKED_ARGB);
|
||||
cmsDoTransform(transform,
|
||||
(PRUint8 *)&packed + 1, (PRUint8 *)&packed + 1,
|
||||
1);
|
||||
gfxRGBA cms(packed, gfxRGBA::PACKED_ARGB);
|
||||
#endif
|
||||
cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, cms.a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
|
||||
}
|
||||
|
||||
|
@ -63,9 +63,16 @@
|
||||
#endif
|
||||
|
||||
#include "cairo.h"
|
||||
#include "lcms.h"
|
||||
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
|
||||
gfxPlatform *gPlatform = nsnull;
|
||||
int gGlitzState = -1;
|
||||
static cmsHPROFILE gCMSOutputProfile = nsnull;
|
||||
static cmsHTRANSFORM gCMSRGBTransform = nsnull;
|
||||
static cmsHTRANSFORM gCMSRGBATransform = nsnull;
|
||||
|
||||
gfxPlatform*
|
||||
gfxPlatform::GetPlatform()
|
||||
@ -296,3 +303,106 @@ gfxPlatform::GetPrefFonts(const char *aLangGroup, nsString& aFonts, PRBool aAppe
|
||||
AppendGenericFontFromPref(aFonts, "x-unicode", nsnull);
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfxPlatform::IsCMSEnabled()
|
||||
{
|
||||
static PRBool sEnabled = -1;
|
||||
if (sEnabled == -1) {
|
||||
sEnabled = PR_TRUE;
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (prefs) {
|
||||
PRBool enabled;
|
||||
nsresult rv =
|
||||
prefs->GetBoolPref("gfx.color_management.enabled", &enabled);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
sEnabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sEnabled;
|
||||
}
|
||||
|
||||
cmsHPROFILE
|
||||
gfxPlatform::GetPlatformCMSOutputProfile()
|
||||
{
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
cmsHPROFILE
|
||||
gfxPlatform::GetCMSOutputProfile()
|
||||
{
|
||||
if (!gCMSOutputProfile) {
|
||||
/* Default lcms error action is to abort on error - change */
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
if (!gCMSOutputProfile) {
|
||||
gCMSOutputProfile =
|
||||
gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfile();
|
||||
}
|
||||
|
||||
if (!gCMSOutputProfile) {
|
||||
gCMSOutputProfile = cmsCreate_sRGBProfile();
|
||||
}
|
||||
}
|
||||
|
||||
return gCMSOutputProfile;
|
||||
}
|
||||
|
||||
cmsHTRANSFORM
|
||||
gfxPlatform::GetCMSRGBTransform()
|
||||
{
|
||||
if (!gCMSRGBTransform) {
|
||||
cmsHPROFILE inProfile, outProfile;
|
||||
outProfile = GetCMSOutputProfile();
|
||||
inProfile = cmsCreate_sRGBProfile();
|
||||
|
||||
if (!inProfile || !outProfile)
|
||||
return nsnull;
|
||||
|
||||
gCMSRGBTransform = cmsCreateTransform(inProfile, TYPE_RGB_8,
|
||||
outProfile, TYPE_RGB_8,
|
||||
INTENT_PERCEPTUAL, 0);
|
||||
}
|
||||
|
||||
return gCMSRGBTransform;
|
||||
}
|
||||
|
||||
cmsHTRANSFORM
|
||||
gfxPlatform::GetCMSRGBATransform()
|
||||
{
|
||||
if (!gCMSRGBATransform) {
|
||||
cmsHPROFILE inProfile, outProfile;
|
||||
outProfile = GetCMSOutputProfile();
|
||||
inProfile = cmsCreate_sRGBProfile();
|
||||
|
||||
if (!inProfile || !outProfile)
|
||||
return nsnull;
|
||||
|
||||
gCMSRGBATransform = cmsCreateTransform(inProfile, TYPE_RGBA_8,
|
||||
outProfile, TYPE_RGBA_8,
|
||||
INTENT_PERCEPTUAL, 0);
|
||||
}
|
||||
|
||||
return gCMSRGBATransform;
|
||||
}
|
||||
|
@ -68,6 +68,8 @@
|
||||
|
||||
#include "nsMathUtils.h"
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
PRInt32 gfxPlatformGtk::sDPI = -1;
|
||||
gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nsnull;
|
||||
|
||||
@ -365,3 +367,130 @@ gfxPlatformGtk::InitDPI()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmsHPROFILE
|
||||
gfxPlatformGtk::GetPlatformCMSOutputProfile()
|
||||
{
|
||||
const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA";
|
||||
const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE";
|
||||
|
||||
Atom edidAtom, iccAtom;
|
||||
Display *dpy = GDK_DISPLAY();
|
||||
Window root = gdk_x11_get_default_root_xwindow();
|
||||
|
||||
Atom retAtom;
|
||||
int retFormat;
|
||||
unsigned long retLength, retAfter;
|
||||
unsigned char *retProperty ;
|
||||
|
||||
iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE);
|
||||
if (iccAtom) {
|
||||
// read once to get size, once for the data
|
||||
if (Success == XGetWindowProperty(dpy, root, iccAtom,
|
||||
0, 0 /* length */,
|
||||
False, AnyPropertyType,
|
||||
&retAtom, &retFormat, &retLength,
|
||||
&retAfter, &retProperty)) {
|
||||
XGetWindowProperty(dpy, root, iccAtom,
|
||||
0, retLength,
|
||||
False, AnyPropertyType,
|
||||
&retAtom, &retFormat, &retLength,
|
||||
&retAfter, &retProperty);
|
||||
|
||||
cmsHPROFILE profile =
|
||||
cmsOpenProfileFromMem(retProperty, retLength);
|
||||
|
||||
XFree(retProperty);
|
||||
|
||||
if (profile) {
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s successfully\n",
|
||||
ICC_PROFILE_ATOM_NAME);
|
||||
#endif
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE);
|
||||
if (edidAtom) {
|
||||
if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32,
|
||||
False, AnyPropertyType,
|
||||
&retAtom, &retFormat, &retLength,
|
||||
&retAfter, &retProperty)) {
|
||||
double gamma;
|
||||
cmsCIExyY whitePoint;
|
||||
cmsCIExyYTRIPLE primaries;
|
||||
|
||||
if (retLength != 128) {
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "Short EDID data\n");
|
||||
#endif
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// Format documented in "VESA E-EDID Implementation Guide"
|
||||
|
||||
gamma = (100 + retProperty[0x17]) / 100.0;
|
||||
whitePoint.x = ((retProperty[0x21] << 2) |
|
||||
(retProperty[0x1a] >> 2 & 3)) / 1024.0;
|
||||
whitePoint.y = ((retProperty[0x22] << 2) |
|
||||
(retProperty[0x1a] >> 0 & 3)) / 1024.0;
|
||||
whitePoint.Y = 1.0;
|
||||
|
||||
primaries.Red.x = ((retProperty[0x1b] << 2) |
|
||||
(retProperty[0x19] >> 6 & 3)) / 1024.0;
|
||||
primaries.Red.y = ((retProperty[0x1c] << 2) |
|
||||
(retProperty[0x19] >> 4 & 3)) / 1024.0;
|
||||
primaries.Red.Y = 1.0;
|
||||
|
||||
primaries.Green.x = ((retProperty[0x1d] << 2) |
|
||||
(retProperty[0x19] >> 2 & 3)) / 1024.0;
|
||||
primaries.Green.y = ((retProperty[0x1e] << 2) |
|
||||
(retProperty[0x19] >> 0 & 3)) / 1024.0;
|
||||
primaries.Green.Y = 1.0;
|
||||
|
||||
primaries.Blue.x = ((retProperty[0x1f] << 2) |
|
||||
(retProperty[0x1a] >> 6 & 3)) / 1024.0;
|
||||
primaries.Blue.y = ((retProperty[0x20] << 2) |
|
||||
(retProperty[0x1a] >> 4 & 3)) / 1024.0;
|
||||
primaries.Blue.Y = 1.0;
|
||||
|
||||
XFree(retProperty);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "EDID gamma: %f\n", gamma);
|
||||
fprintf(stderr, "EDID whitepoint: %f %f %f\n",
|
||||
whitePoint.x, whitePoint.y, whitePoint.Y);
|
||||
fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n",
|
||||
primaries.Red.x, primaries.Red.y, primaries.Red.Y,
|
||||
primaries.Green.x, primaries.Green.y, primaries.Green.Y,
|
||||
primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y);
|
||||
#endif
|
||||
|
||||
LPGAMMATABLE gammaTable[3];
|
||||
gammaTable[0] = gammaTable[1] = gammaTable[2] =
|
||||
cmsBuildGamma(256, gamma);
|
||||
|
||||
if (!gammaTable[0])
|
||||
return nsnull;
|
||||
|
||||
cmsHPROFILE profile =
|
||||
cmsCreateRGBProfile(&whitePoint, &primaries, gammaTable);
|
||||
|
||||
cmsFreeGamma(gammaTable[0]);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
if (profile) {
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s successfully\n",
|
||||
EDID1_ATOM_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
@ -49,6 +49,8 @@
|
||||
#include "glitz-agl.h"
|
||||
#endif
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
gfxPlatformMac::gfxPlatformMac()
|
||||
{
|
||||
#ifdef MOZ_ENABLE_GLITZ
|
||||
@ -174,3 +176,50 @@ gfxPlatformMac::UpdateFontList()
|
||||
gfxQuartzFontCache::SharedFontCache()->UpdateFontList();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
cmsHPROFILE
|
||||
gfxPlatformMac::GetPlatformCMSOutputProfile()
|
||||
{
|
||||
CMProfileLocation device;
|
||||
CMError err = CMGetDeviceProfile(cmDisplayDeviceClass,
|
||||
cmDefaultDeviceID,
|
||||
cmDefaultProfileID,
|
||||
&device);
|
||||
if (err != noErr)
|
||||
return nsnull;
|
||||
|
||||
cmsHPROFILE profile = nsnull;
|
||||
switch (device.locType) {
|
||||
case cmFileBasedProfile: {
|
||||
FSRef fsRef;
|
||||
if (!FSpMakeFSRef(&device.u.fileLoc.spec, &fsRef)) {
|
||||
char path[512];
|
||||
if (!FSRefMakePath(&fsRef, (UInt8*)(path), sizeof(path))) {
|
||||
profile = cmsOpenProfileFromFile(path, "r");
|
||||
#ifdef DEBUG_tor
|
||||
if (profile)
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s fileLoc successfully\n", path);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cmPathBasedProfile:
|
||||
profile = cmsOpenProfileFromFile(device.u.pathLoc.path, "r");
|
||||
#ifdef DEBUG_tor
|
||||
if (profile)
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s pathLoc successfully\n",
|
||||
device.u.pathLoc.path);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "Unhandled ColorSync profile location\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
@ -54,6 +54,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
//#define DEBUG_CMAP_SIZE 1
|
||||
|
||||
/* Define this if we want to update the unicode range bitsets based
|
||||
@ -742,3 +744,24 @@ gfxWindowsPlatform::FindFontEntry(const nsAString& aName)
|
||||
}
|
||||
return fe.get();
|
||||
}
|
||||
|
||||
cmsHPROFILE
|
||||
gfxWindowsPlatform::GetPlatformCMSOutputProfile()
|
||||
{
|
||||
WCHAR str[1024+1];
|
||||
DWORD size = 1024;
|
||||
|
||||
HDC dc = GetDC(nsnull);
|
||||
GetICMProfileW(dc, &size, (LPWSTR)&str);
|
||||
ReleaseDC(nsnull, dc);
|
||||
|
||||
cmsHPROFILE profile =
|
||||
cmsOpenProfileFromFile(NS_ConvertUTF16toUTF8(str).get(), "r");
|
||||
#ifdef DEBUG_tor
|
||||
if (profile)
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s successfully\n",
|
||||
NS_ConvertUTF16toUTF8(str).get());
|
||||
#endif
|
||||
return profile;
|
||||
}
|
||||
|
@ -57,9 +57,7 @@
|
||||
#define PNG_NO_READ_RGB_TO_GRAY
|
||||
#define PNG_NO_READ_USER_TRANSFORM
|
||||
#define PNG_NO_READ_bKGD
|
||||
#define PNG_NO_READ_cHRM
|
||||
#define PNG_NO_READ_hIST
|
||||
#define PNG_NO_READ_iCCP
|
||||
#define PNG_NO_READ_pCAL
|
||||
#define PNG_NO_READ_pHYs
|
||||
#define PNG_NO_READ_sBIT
|
||||
|
@ -61,6 +61,7 @@ REQUIRES = xpcom \
|
||||
$(JPEG_REQUIRES) \
|
||||
$(PNG_REQUIRES) \
|
||||
$(ZLIB_REQUIRES) \
|
||||
$(LCMS_REQUIRES) \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
@ -96,6 +97,7 @@ EXTRA_DSO_LDOPTS = \
|
||||
$(LIBS_DIR) \
|
||||
$(JPEG_LIBS) \
|
||||
$(PNG_LIBS) $(ZLIB_LIBS) \
|
||||
$(LCMS_LIBS) \
|
||||
$(EXTRA_DSO_LIBS) \
|
||||
$(MOZ_COMPONENT_LIBS) \
|
||||
$(NULL)
|
||||
|
@ -54,6 +54,7 @@ REQUIRES = xpcom \
|
||||
thebes \
|
||||
cairo \
|
||||
imglib2 \
|
||||
$(LCMS_REQUIRES) \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = nsGIFDecoder2.cpp
|
||||
|
@ -86,6 +86,9 @@ mailing address.
|
||||
|
||||
#include "imgContainer.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "lcms.h"
|
||||
|
||||
/*
|
||||
* GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
|
||||
*
|
||||
@ -817,17 +820,29 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
||||
GETN(size, gif_global_colormap);
|
||||
break;
|
||||
}
|
||||
// Copy everything and directly go to gif_lzw_start
|
||||
// Copy everything, go to colormap state to do CMS correction
|
||||
memcpy(mGIFStruct.global_colormap, buf, size);
|
||||
buf += size;
|
||||
len -= size;
|
||||
GETN(0, gif_global_colormap);
|
||||
break;
|
||||
}
|
||||
|
||||
GETN(1, gif_image_start);
|
||||
break;
|
||||
|
||||
case gif_global_colormap:
|
||||
// Everything is already copied into global_colormap
|
||||
if (gfxPlatform::IsCMSEnabled()) {
|
||||
// Everything is already copied into global_colormap
|
||||
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
||||
if (transform) {
|
||||
cmsDoTransform(transform,
|
||||
mGIFStruct.global_colormap,
|
||||
mGIFStruct.global_colormap,
|
||||
mGIFStruct.global_colormap_size);
|
||||
}
|
||||
}
|
||||
|
||||
GETN(1, gif_image_start);
|
||||
break;
|
||||
|
||||
@ -1069,10 +1084,12 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
||||
GETN(size, gif_image_colormap);
|
||||
break;
|
||||
}
|
||||
// Copy everything and directly go to gif_lzw_start
|
||||
// Copy everything, go to colormap state to do CMS correction
|
||||
memcpy(mGIFStruct.local_colormap, buf, size);
|
||||
buf += size;
|
||||
len -= size;
|
||||
GETN(0, gif_image_colormap);
|
||||
break;
|
||||
} else {
|
||||
/* Switch back to the global palette */
|
||||
mGIFStruct.is_local_colormap_defined = PR_FALSE;
|
||||
@ -1081,7 +1098,17 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
|
||||
break;
|
||||
|
||||
case gif_image_colormap:
|
||||
// Everything is already copied into local_colormap
|
||||
if (gfxPlatform::IsCMSEnabled()) {
|
||||
// Everything is already copied into local_colormap
|
||||
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
||||
if (transform) {
|
||||
cmsDoTransform(transform,
|
||||
mGIFStruct.local_colormap,
|
||||
mGIFStruct.local_colormap,
|
||||
mGIFStruct.local_colormap_size);
|
||||
}
|
||||
}
|
||||
|
||||
GETN(1, gif_lzw_start);
|
||||
break;
|
||||
|
||||
|
@ -54,9 +54,12 @@ REQUIRES = xpcom \
|
||||
thebes \
|
||||
imglib2 \
|
||||
$(JPEG_REQUIRES) \
|
||||
$(LCMS_REQUIRES) \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = nsJPEGDecoder.cpp
|
||||
|
||||
CSRCS = iccjpeg.c
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
248
modules/libpr0n/decoders/jpeg/iccjpeg.c
Normal file
248
modules/libpr0n/decoders/jpeg/iccjpeg.c
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* iccprofile.c
|
||||
*
|
||||
* This file provides code to read and write International Color Consortium
|
||||
* (ICC) device profiles embedded in JFIF JPEG image files. The ICC has
|
||||
* defined a standard format for including such data in JPEG "APP2" markers.
|
||||
* The code given here does not know anything about the internal structure
|
||||
* of the ICC profile data; it just knows how to put the profile data into
|
||||
* a JPEG file being written, or get it back out when reading.
|
||||
*
|
||||
* This code depends on new features added to the IJG JPEG library as of
|
||||
* IJG release 6b; it will not compile or work with older IJG versions.
|
||||
*
|
||||
* NOTE: this code would need surgery to work on 16-bit-int machines
|
||||
* with ICC profiles exceeding 64K bytes in size. If you need to do that,
|
||||
* change all the "unsigned int" variables to "INT32". You'll also need
|
||||
* to find a malloc() replacement that can allocate more than 64K.
|
||||
*/
|
||||
|
||||
#include "iccjpeg.h"
|
||||
#include <stdlib.h> /* define malloc() */
|
||||
|
||||
|
||||
/*
|
||||
* Since an ICC profile can be larger than the maximum size of a JPEG marker
|
||||
* (64K), we need provisions to split it into multiple markers. The format
|
||||
* defined by the ICC specifies one or more APP2 markers containing the
|
||||
* following data:
|
||||
* Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
|
||||
* Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
|
||||
* Number of markers Total number of APP2's used (1 byte)
|
||||
* Profile data (remainder of APP2 data)
|
||||
* Decoders should use the marker sequence numbers to reassemble the profile,
|
||||
* rather than assuming that the APP2 markers appear in the correct sequence.
|
||||
*/
|
||||
|
||||
#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
|
||||
#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
|
||||
#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
|
||||
#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
|
||||
|
||||
|
||||
/*
|
||||
* This routine writes the given ICC profile data into a JPEG file.
|
||||
* It *must* be called AFTER calling jpeg_start_compress() and BEFORE
|
||||
* the first call to jpeg_write_scanlines().
|
||||
* (This ordering ensures that the APP2 marker(s) will appear after the
|
||||
* SOI and JFIF or Adobe markers, but before all else.)
|
||||
*/
|
||||
|
||||
void
|
||||
write_icc_profile (j_compress_ptr cinfo,
|
||||
const JOCTET *icc_data_ptr,
|
||||
unsigned int icc_data_len)
|
||||
{
|
||||
unsigned int num_markers; /* total number of markers we'll write */
|
||||
int cur_marker = 1; /* per spec, counting starts at 1 */
|
||||
unsigned int length; /* number of bytes to write in this marker */
|
||||
|
||||
/* Calculate the number of markers we'll need, rounding up of course */
|
||||
num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
|
||||
if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
|
||||
num_markers++;
|
||||
|
||||
while (icc_data_len > 0) {
|
||||
/* length of profile to put in this marker */
|
||||
length = icc_data_len;
|
||||
if (length > MAX_DATA_BYTES_IN_MARKER)
|
||||
length = MAX_DATA_BYTES_IN_MARKER;
|
||||
icc_data_len -= length;
|
||||
|
||||
/* Write the JPEG marker header (APP2 code and marker length) */
|
||||
jpeg_write_m_header(cinfo, ICC_MARKER,
|
||||
(unsigned int) (length + ICC_OVERHEAD_LEN));
|
||||
|
||||
/* Write the marker identifying string "ICC_PROFILE" (null-terminated).
|
||||
* We code it in this less-than-transparent way so that the code works
|
||||
* even if the local character set is not ASCII.
|
||||
*/
|
||||
jpeg_write_m_byte(cinfo, 0x49);
|
||||
jpeg_write_m_byte(cinfo, 0x43);
|
||||
jpeg_write_m_byte(cinfo, 0x43);
|
||||
jpeg_write_m_byte(cinfo, 0x5F);
|
||||
jpeg_write_m_byte(cinfo, 0x50);
|
||||
jpeg_write_m_byte(cinfo, 0x52);
|
||||
jpeg_write_m_byte(cinfo, 0x4F);
|
||||
jpeg_write_m_byte(cinfo, 0x46);
|
||||
jpeg_write_m_byte(cinfo, 0x49);
|
||||
jpeg_write_m_byte(cinfo, 0x4C);
|
||||
jpeg_write_m_byte(cinfo, 0x45);
|
||||
jpeg_write_m_byte(cinfo, 0x0);
|
||||
|
||||
/* Add the sequencing info */
|
||||
jpeg_write_m_byte(cinfo, cur_marker);
|
||||
jpeg_write_m_byte(cinfo, (int) num_markers);
|
||||
|
||||
/* Add the profile data */
|
||||
while (length--) {
|
||||
jpeg_write_m_byte(cinfo, *icc_data_ptr);
|
||||
icc_data_ptr++;
|
||||
}
|
||||
cur_marker++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for reading an ICC profile
|
||||
*/
|
||||
|
||||
void
|
||||
setup_read_icc_profile (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* Tell the library to keep any APP2 data it may find */
|
||||
jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handy subroutine to test whether a saved marker is an ICC profile marker.
|
||||
*/
|
||||
|
||||
static boolean
|
||||
marker_is_icc (jpeg_saved_marker_ptr marker)
|
||||
{
|
||||
return
|
||||
marker->marker == ICC_MARKER &&
|
||||
marker->data_length >= ICC_OVERHEAD_LEN &&
|
||||
/* verify the identifying string */
|
||||
GETJOCTET(marker->data[0]) == 0x49 &&
|
||||
GETJOCTET(marker->data[1]) == 0x43 &&
|
||||
GETJOCTET(marker->data[2]) == 0x43 &&
|
||||
GETJOCTET(marker->data[3]) == 0x5F &&
|
||||
GETJOCTET(marker->data[4]) == 0x50 &&
|
||||
GETJOCTET(marker->data[5]) == 0x52 &&
|
||||
GETJOCTET(marker->data[6]) == 0x4F &&
|
||||
GETJOCTET(marker->data[7]) == 0x46 &&
|
||||
GETJOCTET(marker->data[8]) == 0x49 &&
|
||||
GETJOCTET(marker->data[9]) == 0x4C &&
|
||||
GETJOCTET(marker->data[10]) == 0x45 &&
|
||||
GETJOCTET(marker->data[11]) == 0x0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* See if there was an ICC profile in the JPEG file being read;
|
||||
* if so, reassemble and return the profile data.
|
||||
*
|
||||
* TRUE is returned if an ICC profile was found, FALSE if not.
|
||||
* If TRUE is returned, *icc_data_ptr is set to point to the
|
||||
* returned data, and *icc_data_len is set to its length.
|
||||
*
|
||||
* IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
|
||||
* and must be freed by the caller with free() when the caller no longer
|
||||
* needs it. (Alternatively, we could write this routine to use the
|
||||
* IJG library's memory allocator, so that the data would be freed implicitly
|
||||
* at jpeg_finish_decompress() time. But it seems likely that many apps
|
||||
* will prefer to have the data stick around after decompression finishes.)
|
||||
*
|
||||
* NOTE: if the file contains invalid ICC APP2 markers, we just silently
|
||||
* return FALSE. You might want to issue an error message instead.
|
||||
*/
|
||||
|
||||
boolean
|
||||
read_icc_profile (j_decompress_ptr cinfo,
|
||||
JOCTET **icc_data_ptr,
|
||||
unsigned int *icc_data_len)
|
||||
{
|
||||
jpeg_saved_marker_ptr marker;
|
||||
int num_markers = 0;
|
||||
int seq_no;
|
||||
JOCTET *icc_data;
|
||||
unsigned int total_length;
|
||||
#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */
|
||||
char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */
|
||||
unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
|
||||
unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
|
||||
|
||||
*icc_data_ptr = NULL; /* avoid confusion if FALSE return */
|
||||
*icc_data_len = 0;
|
||||
|
||||
/* This first pass over the saved markers discovers whether there are
|
||||
* any ICC markers and verifies the consistency of the marker numbering.
|
||||
*/
|
||||
|
||||
for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
|
||||
marker_present[seq_no] = 0;
|
||||
|
||||
for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker_is_icc(marker)) {
|
||||
if (num_markers == 0)
|
||||
num_markers = GETJOCTET(marker->data[13]);
|
||||
else if (num_markers != GETJOCTET(marker->data[13]))
|
||||
return FALSE; /* inconsistent num_markers fields */
|
||||
seq_no = GETJOCTET(marker->data[12]);
|
||||
if (seq_no <= 0 || seq_no > num_markers)
|
||||
return FALSE; /* bogus sequence number */
|
||||
if (marker_present[seq_no])
|
||||
return FALSE; /* duplicate sequence numbers */
|
||||
marker_present[seq_no] = 1;
|
||||
data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_markers == 0)
|
||||
return FALSE;
|
||||
|
||||
/* Check for missing markers, count total space needed,
|
||||
* compute offset of each marker's part of the data.
|
||||
*/
|
||||
|
||||
total_length = 0;
|
||||
for (seq_no = 1; seq_no <= num_markers; seq_no++) {
|
||||
if (marker_present[seq_no] == 0)
|
||||
return FALSE; /* missing sequence number */
|
||||
data_offset[seq_no] = total_length;
|
||||
total_length += data_length[seq_no];
|
||||
}
|
||||
|
||||
if (total_length <= 0)
|
||||
return FALSE; /* found only empty markers? */
|
||||
|
||||
/* Allocate space for assembled data */
|
||||
icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
|
||||
if (icc_data == NULL)
|
||||
return FALSE; /* oops, out of memory */
|
||||
|
||||
/* and fill it in */
|
||||
for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker_is_icc(marker)) {
|
||||
JOCTET FAR *src_ptr;
|
||||
JOCTET *dst_ptr;
|
||||
unsigned int length;
|
||||
seq_no = GETJOCTET(marker->data[12]);
|
||||
dst_ptr = icc_data + data_offset[seq_no];
|
||||
src_ptr = marker->data + ICC_OVERHEAD_LEN;
|
||||
length = data_length[seq_no];
|
||||
while (length--) {
|
||||
*dst_ptr++ = *src_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*icc_data_ptr = icc_data;
|
||||
*icc_data_len = total_length;
|
||||
|
||||
return TRUE;
|
||||
}
|
73
modules/libpr0n/decoders/jpeg/iccjpeg.h
Normal file
73
modules/libpr0n/decoders/jpeg/iccjpeg.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* iccprofile.h
|
||||
*
|
||||
* This file provides code to read and write International Color Consortium
|
||||
* (ICC) device profiles embedded in JFIF JPEG image files. The ICC has
|
||||
* defined a standard format for including such data in JPEG "APP2" markers.
|
||||
* The code given here does not know anything about the internal structure
|
||||
* of the ICC profile data; it just knows how to put the profile data into
|
||||
* a JPEG file being written, or get it back out when reading.
|
||||
*
|
||||
* This code depends on new features added to the IJG JPEG library as of
|
||||
* IJG release 6b; it will not compile or work with older IJG versions.
|
||||
*
|
||||
* NOTE: this code would need surgery to work on 16-bit-int machines
|
||||
* with ICC profiles exceeding 64K bytes in size. See iccprofile.c
|
||||
* for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* needed to define "FILE", "NULL" */
|
||||
#include "jpeglib.h"
|
||||
|
||||
|
||||
/*
|
||||
* This routine writes the given ICC profile data into a JPEG file.
|
||||
* It *must* be called AFTER calling jpeg_start_compress() and BEFORE
|
||||
* the first call to jpeg_write_scanlines().
|
||||
* (This ordering ensures that the APP2 marker(s) will appear after the
|
||||
* SOI and JFIF or Adobe markers, but before all else.)
|
||||
*/
|
||||
|
||||
extern void write_icc_profile JPP((j_compress_ptr cinfo,
|
||||
const JOCTET *icc_data_ptr,
|
||||
unsigned int icc_data_len));
|
||||
|
||||
|
||||
/*
|
||||
* Reading a JPEG file that may contain an ICC profile requires two steps:
|
||||
*
|
||||
* 1. After jpeg_create_decompress() but before jpeg_read_header(),
|
||||
* call setup_read_icc_profile(). This routine tells the IJG library
|
||||
* to save in memory any APP2 markers it may find in the file.
|
||||
*
|
||||
* 2. After jpeg_read_header(), call read_icc_profile() to find out
|
||||
* whether there was a profile and obtain it if so.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for reading an ICC profile
|
||||
*/
|
||||
|
||||
extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo));
|
||||
|
||||
|
||||
/*
|
||||
* See if there was an ICC profile in the JPEG file being read;
|
||||
* if so, reassemble and return the profile data.
|
||||
*
|
||||
* TRUE is returned if an ICC profile was found, FALSE if not.
|
||||
* If TRUE is returned, *icc_data_ptr is set to point to the
|
||||
* returned data, and *icc_data_len is set to its length.
|
||||
*
|
||||
* IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
|
||||
* and must be freed by the caller with free() when the caller no longer
|
||||
* needs it. (Alternatively, we could write this routine to use the
|
||||
* IJG library's memory allocator, so that the data would be freed implicitly
|
||||
* at jpeg_finish_decompress() time. But it seems likely that many apps
|
||||
* will prefer to have the data stick around after decompression finishes.)
|
||||
*/
|
||||
|
||||
extern boolean read_icc_profile JPP((j_decompress_ptr cinfo,
|
||||
JOCTET **icc_data_ptr,
|
||||
unsigned int *icc_data_len));
|
@ -53,6 +53,12 @@
|
||||
|
||||
#include "jerror.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
extern "C" {
|
||||
#include "iccjpeg.h"
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsJPEGDecoder, imgIDecoder)
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
@ -89,12 +95,19 @@ nsJPEGDecoder::nsJPEGDecoder()
|
||||
|
||||
mBackBuffer = nsnull;
|
||||
mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
|
||||
|
||||
mInProfile = nsnull;
|
||||
mTransform = nsnull;
|
||||
}
|
||||
|
||||
nsJPEGDecoder::~nsJPEGDecoder()
|
||||
{
|
||||
PR_FREEIF(mBuffer);
|
||||
PR_FREEIF(mBackBuffer);
|
||||
if (mTransform)
|
||||
cmsDeleteTransform(mTransform);
|
||||
if (mInProfile)
|
||||
cmsCloseProfile(mInProfile);
|
||||
}
|
||||
|
||||
|
||||
@ -132,6 +145,10 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad)
|
||||
mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
|
||||
mSourceMgr.term_source = term_source;
|
||||
|
||||
/* Record app markers for ICC data */
|
||||
for (PRUint32 m = 0; m < 16; m++)
|
||||
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -223,18 +240,107 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||
if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED)
|
||||
return NS_OK; /* I/O suspension */
|
||||
|
||||
/* let libjpeg take care of gray->RGB and YCbCr->RGB conversions */
|
||||
switch (mInfo.jpeg_color_space) {
|
||||
JOCTET *profile;
|
||||
PRUint32 profileLength;
|
||||
|
||||
if (gfxPlatform::IsCMSEnabled() &&
|
||||
read_icc_profile(&mInfo, &profile, &profileLength) &&
|
||||
(mInProfile = cmsOpenProfileFromMem(profile, profileLength)) != NULL) {
|
||||
free(profile);
|
||||
|
||||
PRUint32 profileSpace = cmsGetColorSpace(mInProfile);
|
||||
PRBool mismatch = PR_FALSE;
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace);
|
||||
#endif
|
||||
switch (mInfo.jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
if (profileSpace == icSigRgbData)
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
else if (profileSpace != icSigGrayData)
|
||||
mismatch = PR_TRUE;
|
||||
break;
|
||||
case JCS_RGB:
|
||||
if (profileSpace != icSigRgbData)
|
||||
mismatch = PR_TRUE;
|
||||
break;
|
||||
case JCS_YCbCr:
|
||||
if (profileSpace == icSigRgbData)
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
else if (profileSpace != icSigYCbCrData)
|
||||
mismatch = PR_TRUE;
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
if (profileSpace == icSigCmykData)
|
||||
mInfo.out_color_space = JCS_CMYK;
|
||||
else
|
||||
mismatch = PR_TRUE;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (!mismatch) {
|
||||
PRUint32 space, channels;
|
||||
switch (mInfo.out_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
space = PT_GRAY;
|
||||
channels = 1;
|
||||
break;
|
||||
case JCS_RGB:
|
||||
space = PT_RGB;
|
||||
channels = 3;
|
||||
break;
|
||||
case JCS_YCbCr:
|
||||
space = PT_YCbCr;
|
||||
channels = 3;
|
||||
case JCS_CMYK:
|
||||
space = PT_CMYK;
|
||||
channels = 4;
|
||||
break;
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
PRUint32 type =
|
||||
COLORSPACE_SH(space) |
|
||||
CHANNELS_SH(channels) |
|
||||
BYTES_SH(1);
|
||||
|
||||
/* Adobe Photoshop writes CMYK files with inverted data */
|
||||
if (mInfo.jpeg_color_space == JCS_CMYK)
|
||||
type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0);
|
||||
|
||||
if (gfxPlatform::GetCMSOutputProfile())
|
||||
mTransform = cmsCreateTransform(mInProfile,
|
||||
type,
|
||||
gfxPlatform::GetCMSOutputProfile(),
|
||||
TYPE_RGB_8,
|
||||
cmsTakeRenderingIntent(mInProfile),
|
||||
0);
|
||||
} else {
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "ICM profile colorspace mismatch\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!mTransform) {
|
||||
switch (mInfo.jpeg_color_space) {
|
||||
case JCS_GRAYSCALE:
|
||||
case JCS_RGB:
|
||||
case JCS_YCbCr:
|
||||
mInfo.out_color_space = JCS_RGB;
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
case JCS_YCCK:
|
||||
default:
|
||||
mState = JPEG_ERROR;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -479,6 +585,30 @@ nsJPEGDecoder::OutputScanlines()
|
||||
break;
|
||||
}
|
||||
|
||||
if (mTransform) {
|
||||
if (mInfo.out_color_space == JCS_GRAYSCALE) {
|
||||
/* move gray data to end of mSample array so
|
||||
cmsDoTransform can do in-place transform */
|
||||
memcpy(mSamples[0] + 2 * mInfo.output_width,
|
||||
mSamples[0],
|
||||
mInfo.output_width);
|
||||
cmsDoTransform(mTransform,
|
||||
mSamples[0] + 2 * mInfo.output_width, mSamples[0],
|
||||
mInfo.output_width);
|
||||
} else
|
||||
cmsDoTransform(mTransform,
|
||||
mSamples[0], mSamples[0],
|
||||
mInfo.output_width);
|
||||
} else {
|
||||
/* No embedded ICC profile - treat as sRGB */
|
||||
cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform();
|
||||
if (transform) {
|
||||
cmsDoTransform(transform,
|
||||
mSamples[0], mSamples[0],
|
||||
mInfo.output_width);
|
||||
}
|
||||
}
|
||||
|
||||
// offset is in Cairo pixels (PRUint32)
|
||||
PRUint32 offset = (mInfo.output_scanline - 1) * mInfo.output_width;
|
||||
PRUint32 *ptrOutputBuf = ((PRUint32*)imageData) + offset;
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "imgILoad.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "lcms.h"
|
||||
|
||||
extern "C" {
|
||||
#include "jpeglib.h"
|
||||
@ -118,6 +119,12 @@ public:
|
||||
PRUint32 mBackBufferSize; // size in bytes what mBackBuffer was created with
|
||||
PRUint32 mBackBufferUnreadLen; // amount of data currently in mBackBuffer
|
||||
|
||||
JOCTET *mProfile;
|
||||
PRUint32 mProfileLength;
|
||||
|
||||
cmsHPROFILE mInProfile;
|
||||
cmsHTRANSFORM mTransform;
|
||||
|
||||
PRPackedBool mReading;
|
||||
};
|
||||
|
||||
|
@ -57,6 +57,7 @@ REQUIRES = xpcom \
|
||||
imglib2 \
|
||||
$(PNG_REQUIRES) \
|
||||
$(ZLIB_REQUIRES) \
|
||||
$(LCMS_REQUIRES) \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = nsPNGDecoder.cpp
|
||||
|
@ -56,6 +56,8 @@
|
||||
#include "nspr.h"
|
||||
#include "png.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
// for nsPNGDecoder.apngFlags
|
||||
enum {
|
||||
FRAME_HIDDEN = 0x01
|
||||
@ -77,16 +79,25 @@ NS_IMPL_ISUPPORTS1(nsPNGDecoder, imgIDecoder)
|
||||
|
||||
nsPNGDecoder::nsPNGDecoder() :
|
||||
mPNG(nsnull), mInfo(nsnull),
|
||||
apngFlags(0),
|
||||
interlacebuf(nsnull), ibpr(0),
|
||||
mError(PR_FALSE)
|
||||
mCMSLine(nsnull), interlacebuf(nsnull),
|
||||
mInProfile(nsnull), mTransform(nsnull),
|
||||
ibpr(0), apngFlags(0), mChannels(0), mError(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
nsPNGDecoder::~nsPNGDecoder()
|
||||
{
|
||||
if (mCMSLine)
|
||||
nsMemory::Free(mCMSLine);
|
||||
if (interlacebuf)
|
||||
nsMemory::Free(interlacebuf);
|
||||
if (mInProfile) {
|
||||
cmsCloseProfile(mInProfile);
|
||||
|
||||
/* mTransform belongs to us only if mInProfile is non-null */
|
||||
if (mTransform)
|
||||
cmsDeleteTransform(mTransform);
|
||||
}
|
||||
}
|
||||
|
||||
// CreateFrame() is used for both simple and animated images
|
||||
@ -153,9 +164,7 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
|
||||
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
|
||||
static png_byte unused_chunks[]=
|
||||
{ 98, 75, 71, 68, '\0', /* bKGD */
|
||||
99, 72, 82, 77, '\0', /* cHRM */
|
||||
104, 73, 83, 84, '\0', /* hIST */
|
||||
105, 67, 67, 80, '\0', /* iCCP */
|
||||
105, 84, 88, 116, '\0', /* iTXt */
|
||||
111, 70, 70, 115, '\0', /* oFFs */
|
||||
112, 67, 65, 76, '\0', /* pCAL */
|
||||
@ -266,6 +275,119 @@ NS_IMETHODIMP nsPNGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRU
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Adapted from http://www.littlecms.com/pngchrm.c example code
|
||||
static cmsHPROFILE
|
||||
PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
|
||||
int color_type, PRUint32 *inType, PRUint32 *intent)
|
||||
{
|
||||
cmsHPROFILE profile = nsnull;
|
||||
*intent = INTENT_PERCEPTUAL; // XXX: should this be the default?
|
||||
|
||||
// First try to see if iCCP chunk is present
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
|
||||
png_uint_32 profileLen;
|
||||
char *profileData, *profileName;
|
||||
int compression;
|
||||
|
||||
png_get_iCCP(png_ptr, info_ptr, &profileName, &compression,
|
||||
&profileData, &profileLen);
|
||||
|
||||
profile = cmsOpenProfileFromMem(profileData, profileLen);
|
||||
PRUint32 profileSpace = cmsGetColorSpace(profile);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "PNG profileSpace: 0x%08X\n", profileSpace);
|
||||
#endif
|
||||
|
||||
PRBool mismatch = PR_FALSE;
|
||||
if (color_type & PNG_COLOR_MASK_COLOR) {
|
||||
if (profileSpace != icSigRgbData)
|
||||
mismatch = PR_TRUE;
|
||||
} else {
|
||||
if (profileSpace == icSigRgbData)
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
else if (profileSpace != icSigGrayData)
|
||||
mismatch = PR_TRUE;
|
||||
}
|
||||
|
||||
if (mismatch) {
|
||||
cmsCloseProfile(profile);
|
||||
profile = nsnull;
|
||||
} else {
|
||||
*intent = cmsTakeRenderingIntent(profile);
|
||||
}
|
||||
}
|
||||
|
||||
// Check sRGB chunk
|
||||
if (!profile && png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
|
||||
profile = cmsCreate_sRGBProfile();
|
||||
|
||||
if (profile) {
|
||||
int fileIntent;
|
||||
png_get_sRGB(png_ptr, info_ptr, &fileIntent);
|
||||
PRUint32 map[] = { INTENT_PERCEPTUAL, INTENT_RELATIVE_COLORIMETRIC,
|
||||
INTENT_SATURATION, INTENT_ABSOLUTE_COLORIMETRIC };
|
||||
*intent = map[fileIntent];
|
||||
}
|
||||
}
|
||||
|
||||
// Check gAMA/cHRM chunks
|
||||
if (!profile && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
|
||||
cmsCIExyY whitePoint = {0.3127, 0.3290, 1.0}; // D65
|
||||
cmsCIExyYTRIPLE primaries = {
|
||||
{0.6400, 0.3300, 1.0},
|
||||
{0.3000, 0.6000, 1.0},
|
||||
{0.1500, 0.0600, 1.0}
|
||||
};
|
||||
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) {
|
||||
png_get_cHRM(png_ptr, info_ptr,
|
||||
&whitePoint.x, &whitePoint.y,
|
||||
&primaries.Red.x, &primaries.Red.y,
|
||||
&primaries.Green.x, &primaries.Green.y,
|
||||
&primaries.Blue.x, &primaries.Blue.y);
|
||||
|
||||
whitePoint.Y =
|
||||
primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0;
|
||||
}
|
||||
|
||||
double gammaOfFile;
|
||||
LPGAMMATABLE gammaTable[3];
|
||||
|
||||
png_get_gAMA(png_ptr, info_ptr, &gammaOfFile);
|
||||
|
||||
gammaTable[0] = gammaTable[1] = gammaTable[2] =
|
||||
cmsBuildGamma(256, 1/gammaOfFile);
|
||||
|
||||
if (!gammaTable[0])
|
||||
return nsnull;
|
||||
|
||||
profile = cmsCreateRGBProfile(&whitePoint, &primaries, gammaTable);
|
||||
|
||||
if (profile && !(color_type & PNG_COLOR_MASK_COLOR))
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
|
||||
cmsFreeGamma(gammaTable[0]);
|
||||
}
|
||||
|
||||
if (profile) {
|
||||
PRUint32 profileSpace = cmsGetColorSpace(profile);
|
||||
if (profileSpace == icSigGrayData) {
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA)
|
||||
*inType = TYPE_GRAYA_8;
|
||||
else
|
||||
*inType = TYPE_GRAY_8;
|
||||
} else {
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA ||
|
||||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
||||
*inType = TYPE_RGBA_8;
|
||||
else
|
||||
*inType = TYPE_RGB_8;
|
||||
}
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
void
|
||||
info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
@ -305,6 +427,53 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
if (bit_depth == 16)
|
||||
png_set_strip_16(png_ptr);
|
||||
|
||||
PRUint32 inType, intent;
|
||||
if (gfxPlatform::IsCMSEnabled()) {
|
||||
decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr,
|
||||
color_type, &inType, &intent);
|
||||
}
|
||||
if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
|
||||
PRUint32 outType;
|
||||
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA || trans)
|
||||
outType = TYPE_RGBA_8;
|
||||
else
|
||||
outType = TYPE_RGB_8;
|
||||
|
||||
decoder->mTransform = cmsCreateTransform(decoder->mInProfile,
|
||||
inType,
|
||||
gfxPlatform::GetCMSOutputProfile(),
|
||||
outType,
|
||||
intent,
|
||||
0);
|
||||
} else {
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
if (gfxPlatform::IsCMSEnabled()) {
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA || trans)
|
||||
decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
|
||||
else
|
||||
decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
|
||||
}
|
||||
}
|
||||
|
||||
if (!decoder->mTransform) {
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
|
||||
if (png_get_gAMA(png_ptr, info_ptr, &aGamma)) {
|
||||
if ((aGamma <= 0.0) || (aGamma > 21474.83)) {
|
||||
aGamma = 0.45455;
|
||||
png_set_gAMA(png_ptr, info_ptr, aGamma);
|
||||
}
|
||||
png_set_gamma(png_ptr, 2.2, aGamma);
|
||||
}
|
||||
else
|
||||
png_set_gamma(png_ptr, 2.2, 0.45455);
|
||||
}
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
@ -328,8 +497,7 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
/* now all of those things we set above are used to update various struct
|
||||
* members and whatnot, after which we can get channels, rowbytes, etc. */
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
channels = png_get_channels(png_ptr, info_ptr);
|
||||
PR_ASSERT(channels == 3 || channels == 4);
|
||||
decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);
|
||||
|
||||
/*---------------------------------------------------------------*/
|
||||
/* copy PNG info into imagelib structs (formerly png_set_dims()) */
|
||||
@ -337,7 +505,7 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
|
||||
PRInt32 alpha_bits = 1;
|
||||
|
||||
if (channels > 3) {
|
||||
if (channels == 2 || channels == 4) {
|
||||
/* check if alpha is coming from a tRNS chunk and is binary */
|
||||
if (num_trans) {
|
||||
/* if it's not a indexed color image, tRNS means binary */
|
||||
@ -368,9 +536,9 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
if (decoder->mObserver)
|
||||
decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
|
||||
|
||||
if (channels == 3) {
|
||||
if (channels == 1 || channels == 3) {
|
||||
decoder->format = gfxIFormats::RGB;
|
||||
} else if (channels > 3) {
|
||||
} else if (channels == 2 || channels == 4) {
|
||||
if (alpha_bits == 8) {
|
||||
decoder->mImage->GetPreferredAlphaChannelFormat(&(decoder->format));
|
||||
} else if (alpha_bits == 1) {
|
||||
@ -398,11 +566,17 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||
PRUint32 bpr;
|
||||
decoder->mFrame->GetImageBytesPerRow(&bpr);
|
||||
|
||||
if (decoder->mTransform &&
|
||||
(channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
|
||||
PRUint32 bpp[] = { 0, 3, 4, 3, 4 };
|
||||
decoder->mCMSLine =
|
||||
(PRUint8 *)nsMemory::Alloc(bpp[channels] * width);
|
||||
if (!decoder->mCMSLine)
|
||||
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY
|
||||
}
|
||||
|
||||
if (interlace_type == PNG_INTERLACE_ADAM7) {
|
||||
if (channels > 3)
|
||||
decoder->ibpr = channels*width;
|
||||
else
|
||||
decoder->ibpr = bpr;
|
||||
decoder->ibpr = channels * width;
|
||||
decoder->interlacebuf = (PRUint8 *)nsMemory::Alloc(decoder->ibpr*height);
|
||||
if (!decoder->interlacebuf) {
|
||||
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY
|
||||
@ -475,6 +649,21 @@ row_callback(png_structp png_ptr, png_bytep new_row,
|
||||
decoder->mFrame->GetImageData(&imageData, &imageDataLength);
|
||||
PRUint32 *cptr32 = (PRUint32*)(imageData + (row_num*bpr));
|
||||
|
||||
if (decoder->mTransform) {
|
||||
if (decoder->mCMSLine) {
|
||||
cmsDoTransform(decoder->mTransform, line, decoder->mCMSLine, iwidth);
|
||||
/* copy alpha over */
|
||||
PRUint32 channels = decoder->mChannels;
|
||||
if (channels == 2 || channels == 4) {
|
||||
for (PRUint32 i = 0; i < iwidth; i++)
|
||||
decoder->mCMSLine[4 * i + 3] = line[channels * i + channels - 1];
|
||||
}
|
||||
line = decoder->mCMSLine;
|
||||
} else {
|
||||
cmsDoTransform(decoder->mTransform, line, line, iwidth);
|
||||
}
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case gfxIFormats::RGB:
|
||||
case gfxIFormats::BGR:
|
||||
|
@ -52,6 +52,8 @@
|
||||
|
||||
#include "png.h"
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
#define NS_PNGDECODER_CID \
|
||||
{ /* 36fa00c2-1dd2-11b2-be07-d16eeb4c50ed */ \
|
||||
0x36fa00c2, \
|
||||
@ -81,10 +83,15 @@ public:
|
||||
|
||||
png_structp mPNG;
|
||||
png_infop mInfo;
|
||||
PRUint8 *mCMSLine;
|
||||
PRUint8 *interlacebuf;
|
||||
cmsHPROFILE mInProfile;
|
||||
cmsHTRANSFORM mTransform;
|
||||
|
||||
PRUint32 ibpr;
|
||||
gfx_format format;
|
||||
PRUint8 apngFlags;
|
||||
PRUint8 *interlacebuf;
|
||||
PRUint32 ibpr;
|
||||
PRUint8 mChannels;
|
||||
PRPackedBool mError;
|
||||
};
|
||||
|
||||
|
@ -118,6 +118,9 @@ pref("browser.chrome.image_icons.max_size", 1024);
|
||||
|
||||
pref("browser.triple_click_selects_paragraph", true);
|
||||
|
||||
pref("gfx.color_management.enabled", false);
|
||||
pref("gfx.color_management.display_profile", "");
|
||||
|
||||
pref("accessibility.browsewithcaret", false);
|
||||
pref("accessibility.warn_on_browsewithcaret", true);
|
||||
|
||||
|
@ -68,6 +68,10 @@ tier_external_dirs += modules/libbz2
|
||||
tier_external_dirs += modules/libmar
|
||||
endif
|
||||
|
||||
ifndef MOZ_NATIVE_LCMS
|
||||
tier_external_dirs += modules/lcms
|
||||
endif
|
||||
|
||||
#
|
||||
# tier "gecko" - core components
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user