зеркало из https://github.com/mozilla/pjs.git
adding color management capabilities -- preffed off. bug 16769. patch from tor. r=bsmedberg, sr=me
This commit is contained in:
Родитель
3a70ec8c63
Коммит
041108163b
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Main Author:
|
||||
------------
|
||||
Marti Maria <info@littlecms.com>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
Little CMS
|
||||
Copyright (c) 1998-2007 Marti Maria Saguer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,46 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is lcms mozilla build integration.
|
||||
#
|
||||
# The Initial Developer of the Original Code is IBM Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = lcms
|
||||
DIRS = include src
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
New in ver 1.17
|
||||
===============
|
||||
|
||||
Security fixes
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
Read.me for release 1.17
|
||||
========================
|
||||
|
||||
Little cms
|
||||
Copyright (C) 1998-2007 Marti Maria
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
The lcms library is now distributed under
|
||||
|
||||
MIT PUBLIC LICENSE
|
||||
|
||||
See file COPYING. for details
|
||||
|
||||
|
||||
This is the 15th. public release the engine. It has been tested
|
||||
across several versions before, but it is possible some
|
||||
bugs still arises. If so, sorry for the inconvenience, and
|
||||
please feel free to submit any suggestion/solution (if you can
|
||||
found it) at:
|
||||
|
||||
info@littlecms.com
|
||||
|
||||
|
||||
Note that the aesthetics of resulting colors are due only to
|
||||
profiles, and not as consequence of the lcms package.
|
||||
|
||||
The main site for the package is located at
|
||||
|
||||
http://www.littlecms.com
|
||||
or
|
||||
http://www.lcms.coloraid.de
|
||||
|
||||
|
||||
Littlecms has also a mailing list on:
|
||||
|
||||
http://lists.sourceforge.net/lists/listinfo/lcms-user
|
||||
|
||||
|
||||
Looking forward the lcms project would grow in future, I will
|
||||
welcome any contribution/optimization/enhancement.
|
||||
|
||||
Enjoy!
|
||||
|
||||
|
||||
About profiles
|
||||
==============
|
||||
|
||||
The demo of this package includes some profiles for colorspace
|
||||
conversions. I figure all of them are in public domain, but
|
||||
since some contains copyright notice, I will enumerate here
|
||||
the sources:
|
||||
|
||||
Sun Microsystems Java SDK (widely available)
|
||||
Kodak public FTP site: ftp.kodak.com
|
||||
ICM Stress demo from microsoft. www.microsoft.com
|
||||
sRGB from sRGB site www.srgb.com
|
||||
|
||||
If you found some profile of these not to be in public domain,
|
||||
please notify me. I will remove the offending profile as soon as
|
||||
posible.
|
||||
|
||||
|
||||
|
||||
Additional files
|
||||
================
|
||||
|
||||
ICC34.h is the header file the International Color Consortium
|
||||
has posted for version spec 3.4, with some minor modifications
|
||||
for improving portability.
|
||||
|
||||
You can reach it at
|
||||
|
||||
http://www.color.org
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is lcms mozilla build integration.
|
||||
#
|
||||
# The Initial Developer of the Original Code is IBM Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = lcms
|
||||
EXPORTS = icc34.h lcms.h
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,75 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is lcms mozilla build integration.
|
||||
#
|
||||
# The Initial Developer of the Original Code is IBM Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = lcms
|
||||
LIBRARY_NAME = mozlcms
|
||||
|
||||
GRE_MODULE = 1
|
||||
LIBXUL_LIBRARY = 1
|
||||
DIST_INSTALL = 1
|
||||
|
||||
ifeq (,$(MOZ_ENABLE_LIBXUL)$(BUILD_STATIC_LIBS))
|
||||
ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH)))
|
||||
ifndef GNU_CC
|
||||
MAPFILE = $(LIBRARY_NAME).map
|
||||
DEFFILE = $(win_srcdir)/lcms.def
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (,$(filter-out WINNT WINCE OS2,$(OS_ARCH)))
|
||||
DEFINES += -DLCMS_DLL=1 -DLCMS_DLL_BUILD=1
|
||||
else
|
||||
VISIBILITY_FLAGS =
|
||||
endif
|
||||
endif
|
||||
|
||||
REQUIRES = $(LCMS_REQUIRES) \
|
||||
$(NULL)
|
||||
|
||||
CSRCS = cmscnvrt.c cmserr.c cmsgamma.c cmsgmt.c cmsintrp.c cmsio1.c \
|
||||
cmslut.c cmsmatsh.c cmsmtrx.c cmspack.c cmspcs.c cmswtpnt.c \
|
||||
cmsxform.c cmssamp.c cmscam97.c cmsnamed.c cmsps2.c cmscam02.c \
|
||||
cmsvirt.c cmscgats.c cmsio0.c
|
||||
|
||||
LOCAL_INCLUDES += -I../include
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,490 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging.
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC);
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel);
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut);
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut);
|
||||
|
||||
|
||||
// ---------- Implementation --------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
|
||||
double XYZ[3];
|
||||
double RGB[3];
|
||||
double RGBc[3];
|
||||
double RGBp[3];
|
||||
double RGBpa[3];
|
||||
double a, b, h, e, H, A, J, Q, s, t, C, M;
|
||||
double abC[2];
|
||||
double abs[2];
|
||||
double abM[2];
|
||||
|
||||
} CAM02COLOR, *LPCAM02COLOR;
|
||||
|
||||
typedef struct {
|
||||
|
||||
CAM02COLOR adoptedWhite;
|
||||
double LA, Yb;
|
||||
double F, c, Nc;
|
||||
int surround;
|
||||
double n, Nbb, Ncb, z, FL, D;
|
||||
|
||||
} cmsCIECAM02, *LPcmsCIECAM02;
|
||||
|
||||
|
||||
static
|
||||
double compute_n(LPcmsCIECAM02 pMod)
|
||||
{
|
||||
return(pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
|
||||
}
|
||||
|
||||
static
|
||||
double compute_z(LPcmsCIECAM02 pMod)
|
||||
{
|
||||
return(1.48 + pow(pMod -> n, 0.5));
|
||||
}
|
||||
|
||||
static
|
||||
double computeNbb(LPcmsCIECAM02 pMod)
|
||||
{
|
||||
return(0.725 * pow((1.0 / pMod -> n), 0.2));
|
||||
}
|
||||
|
||||
static
|
||||
double computeFL(LPcmsCIECAM02 pMod)
|
||||
{
|
||||
double k, FL;
|
||||
|
||||
k = 1.0 / ((5.0 * pMod->LA) + 1.0);
|
||||
FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 *
|
||||
(pow((1.0 - pow(k, 4.0)), 2.0)) *
|
||||
(pow((5.0 * pMod->LA), (1.0 / 3.0)));
|
||||
|
||||
return FL;
|
||||
}
|
||||
|
||||
static
|
||||
double computeD(LPcmsCIECAM02 pMod)
|
||||
{
|
||||
double D;
|
||||
|
||||
D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0)));
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR XYZtoCAT02(CAM02COLOR clr)
|
||||
{
|
||||
clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624);
|
||||
clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061);
|
||||
clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] *
|
||||
(pMod->D / pMod -> adoptedWhite.RGB[i])) +
|
||||
(1.0 - pMod->D)) * clr.RGB[i];
|
||||
}
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR CAT02toHPE (CAM02COLOR clr)
|
||||
{
|
||||
|
||||
double M[9];
|
||||
|
||||
|
||||
M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628));
|
||||
M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698));
|
||||
M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326));
|
||||
M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628));
|
||||
M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698));
|
||||
M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326));
|
||||
M[6] =(-0.009628);
|
||||
M[7] =(-0.005698);
|
||||
M[8] =( 1.015326);
|
||||
|
||||
clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]);
|
||||
clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]);
|
||||
clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR NonlinearCompression(CAM02COLOR clr, LPcmsCIECAM02 pMod)
|
||||
{
|
||||
int i;
|
||||
double temp;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (clr.RGBp[i] < 0) {
|
||||
|
||||
temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42);
|
||||
clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1;
|
||||
}
|
||||
else {
|
||||
temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42);
|
||||
clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] +
|
||||
(clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb;
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR ComputeCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
|
||||
{
|
||||
double a, b, temp, e, t, r2d, d2r;
|
||||
|
||||
a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0);
|
||||
b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0;
|
||||
|
||||
r2d = (180.0 / 3.141592654);
|
||||
if (a == 0) {
|
||||
if (b == 0) clr.h = 0;
|
||||
else if (b > 0) clr.h = 90;
|
||||
else clr.h = 270;
|
||||
}
|
||||
else if (a > 0) {
|
||||
temp = b / a;
|
||||
if (b > 0) clr.h = (r2d * atan(temp));
|
||||
else if (b == 0) clr.h = 0;
|
||||
else clr.h = (r2d * atan(temp)) + 360;
|
||||
}
|
||||
else {
|
||||
temp = b / a;
|
||||
clr.h = (r2d * atan(temp)) + 180;
|
||||
}
|
||||
|
||||
d2r = (3.141592654 / 180.0);
|
||||
e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
|
||||
(cos((clr.h * d2r + 2.0)) + 3.8);
|
||||
|
||||
if (clr.h < 20.14) {
|
||||
temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8);
|
||||
clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp;
|
||||
}
|
||||
else if (clr.h < 90.0) {
|
||||
temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7);
|
||||
clr.H = (100*((clr.h - 20.14)/0.8)) / temp;
|
||||
}
|
||||
else if (clr.h < 164.25) {
|
||||
temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0);
|
||||
clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp);
|
||||
}
|
||||
else if (clr.h < 237.53) {
|
||||
temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2);
|
||||
clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp);
|
||||
}
|
||||
else {
|
||||
temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8);
|
||||
clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp);
|
||||
}
|
||||
|
||||
clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A),
|
||||
(pMod->c * pMod->z));
|
||||
|
||||
clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) *
|
||||
(pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25);
|
||||
|
||||
t = (e * pow(((a * a) + (b * b)), 0.5)) /
|
||||
(clr.RGBpa[0] + clr.RGBpa[1] +
|
||||
((21.0 / 20.0) * clr.RGBpa[2]));
|
||||
|
||||
clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) *
|
||||
pow((1.64 - pow(0.29, pMod->n)), 0.73);
|
||||
|
||||
clr.M = clr.C * pow(pMod->FL, 0.25);
|
||||
clr.s = 100.0 * pow((clr.M / clr.Q), 0.5);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR InverseCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
|
||||
{
|
||||
|
||||
double t, e, p1, p2, p3, p4, p5, hr, d2r;
|
||||
d2r = 3.141592654 / 180.0;
|
||||
|
||||
t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) *
|
||||
(pow((1.64 - pow(0.29, pMod->n)), 0.73)))),
|
||||
(1.0 / 0.9) );
|
||||
e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
|
||||
(cos((clr.h * d2r + 2.0)) + 3.8);
|
||||
|
||||
clr.A = pMod->adoptedWhite.A * pow(
|
||||
(clr.J / 100.0),
|
||||
(1.0 / (pMod->c * pMod->z)));
|
||||
|
||||
p1 = e / t;
|
||||
p2 = (clr.A / pMod->Nbb) + 0.305;
|
||||
p3 = 21.0 / 20.0;
|
||||
|
||||
hr = clr.h * d2r;
|
||||
|
||||
if (fabs(sin(hr)) >= fabs(cos(hr))) {
|
||||
p4 = p1 / sin(hr);
|
||||
clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
|
||||
(p4 + (2.0 + p3) * (220.0 / 1403.0) *
|
||||
(cos(hr) / sin(hr)) - (27.0 / 1403.0) +
|
||||
p3 * (6300.0 / 1403.0));
|
||||
clr.a = clr.b * (cos(hr) / sin(hr));
|
||||
}
|
||||
else {
|
||||
p5 = p1 / cos(hr);
|
||||
clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
|
||||
(p5 + (2.0 + p3) * (220.0 / 1403.0) -
|
||||
((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
|
||||
(sin(hr) / cos(hr)));
|
||||
clr.b = clr.a * (sin(hr) / cos(hr));
|
||||
}
|
||||
|
||||
clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +
|
||||
((451.0 / 1403.0) * clr.a) +
|
||||
((288.0 / 1403.0) * clr.b);
|
||||
clr.RGBpa[1] = ((460.0 / 1403.0) * p2) -
|
||||
((891.0 / 1403.0) * clr.a) -
|
||||
((261.0 / 1403.0) * clr.b);
|
||||
clr.RGBpa[2] = ((460.0 / 1403.0) * p2) -
|
||||
((220.0 / 1403.0) * clr.a) -
|
||||
((6300.0 / 1403.0) * clr.b);
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR InverseNonlinearity(CAM02COLOR clr, LPcmsCIECAM02 pMod)
|
||||
{
|
||||
int i;
|
||||
double c1;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1;
|
||||
else c1 = 1;
|
||||
clr.RGBp[i] = c1 * (100.0 / pMod->FL) *
|
||||
pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) /
|
||||
(400.0 - fabs(clr.RGBpa[i] - 0.1))),
|
||||
(1.0 / 0.42));
|
||||
}
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
static
|
||||
CAM02COLOR HPEtoCAT02(CAM02COLOR clr)
|
||||
{
|
||||
double M[9];
|
||||
|
||||
M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950));
|
||||
M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054));
|
||||
M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624);
|
||||
M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950));
|
||||
M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054));
|
||||
M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061);
|
||||
M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950));
|
||||
M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054));
|
||||
M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);;
|
||||
|
||||
clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]);
|
||||
clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]);
|
||||
clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]);
|
||||
return (clr);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
clr.RGB[i] = clr.RGBc[i] /
|
||||
((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D);
|
||||
}
|
||||
return(clr);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
|
||||
{
|
||||
clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745);
|
||||
clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098);
|
||||
clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326);
|
||||
|
||||
return(clr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
|
||||
{
|
||||
LPcmsCIECAM02 lpMod;
|
||||
|
||||
|
||||
if((lpMod = (LPcmsCIECAM02) _cmsMalloc(sizeof(cmsCIECAM02))) == NULL) {
|
||||
return (LCMSHANDLE) NULL;
|
||||
}
|
||||
|
||||
|
||||
ZeroMemory(lpMod, sizeof(cmsCIECAM02));
|
||||
|
||||
lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
|
||||
lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
|
||||
lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
|
||||
|
||||
lpMod -> LA = pVC ->La;
|
||||
lpMod -> Yb = pVC ->Yb;
|
||||
lpMod -> D = pVC ->D_value;
|
||||
lpMod -> surround = pVC ->surround;
|
||||
|
||||
switch (lpMod -> surround) {
|
||||
|
||||
case AVG_SURROUND_4:
|
||||
lpMod->F = 1.0; // Not included in CAM02
|
||||
lpMod->c = 0.69;
|
||||
lpMod->Nc = 1.0;
|
||||
break;
|
||||
|
||||
case CUTSHEET_SURROUND:
|
||||
lpMod->F = 0.8;
|
||||
lpMod->c = 0.41;
|
||||
lpMod->Nc = 0.8;
|
||||
break;
|
||||
|
||||
case DARK_SURROUND:
|
||||
lpMod -> F = 0.8;
|
||||
lpMod -> c = 0.525;
|
||||
lpMod -> Nc = 0.8;
|
||||
break;
|
||||
|
||||
|
||||
case DIM_SURROUND:
|
||||
lpMod -> F = 0.9;
|
||||
lpMod -> c = 0.59;
|
||||
lpMod -> Nc = 0.95;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Average surround
|
||||
lpMod -> F = 1.0;
|
||||
lpMod -> c = 0.69;
|
||||
lpMod -> Nc = 1.0;
|
||||
}
|
||||
|
||||
lpMod -> n = compute_n(lpMod);
|
||||
lpMod -> z = compute_z(lpMod);
|
||||
lpMod -> Nbb = computeNbb(lpMod);
|
||||
lpMod -> FL = computeFL(lpMod);
|
||||
|
||||
if (lpMod -> D == D_CALCULATE ||
|
||||
lpMod -> D == D_CALCULATE_DISCOUNT) {
|
||||
|
||||
lpMod -> D = computeD(lpMod);
|
||||
}
|
||||
|
||||
lpMod -> Ncb = lpMod -> Nbb;
|
||||
|
||||
lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
|
||||
lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
|
||||
lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
|
||||
lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
|
||||
|
||||
return (LCMSHANDLE) lpMod;
|
||||
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel)
|
||||
{
|
||||
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
|
||||
if (lpMod) free(lpMod);
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut)
|
||||
{
|
||||
CAM02COLOR clr;
|
||||
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
|
||||
|
||||
clr.XYZ[0] = pIn ->X;
|
||||
clr.XYZ[1] = pIn ->Y;
|
||||
clr.XYZ[2] = pIn ->Z;
|
||||
|
||||
clr = XYZtoCAT02(clr);
|
||||
clr = ChromaticAdaptation(clr, lpMod);
|
||||
clr = CAT02toHPE(clr);
|
||||
clr = NonlinearCompression(clr, lpMod);
|
||||
clr = ComputeCorrelates(clr, lpMod);
|
||||
|
||||
pOut ->J = clr.J;
|
||||
pOut ->C = clr.C;
|
||||
pOut ->h = clr.h;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut)
|
||||
{
|
||||
CAM02COLOR clr;
|
||||
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
|
||||
|
||||
|
||||
clr.J = pIn -> J;
|
||||
clr.C = pIn -> C;
|
||||
clr.h = pIn -> h;
|
||||
|
||||
clr = InverseCorrelates(clr, lpMod);
|
||||
clr = InverseNonlinearity(clr, lpMod);
|
||||
clr = HPEtoCAT02(clr);
|
||||
clr = InverseChromaticAdaptation(clr, lpMod);
|
||||
clr = CAT02toXYZ(clr);
|
||||
|
||||
pOut ->X = clr.XYZ[0];
|
||||
pOut ->Y = clr.XYZ[1];
|
||||
pOut ->Z = clr.XYZ[2];
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,721 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
double J;
|
||||
double C;
|
||||
double h;
|
||||
|
||||
} cmsJCh, FAR* LPcmsJCh;
|
||||
|
||||
|
||||
#define AVG_SURROUND_4 0
|
||||
#define AVG_SURROUND 1
|
||||
#define DIM_SURROUND 2
|
||||
#define DARK_SURROUND 3
|
||||
#define CUTSHEET_SURROUND 4
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsCIEXYZ whitePoint;
|
||||
double Yb;
|
||||
double La;
|
||||
int surround;
|
||||
double D_value;
|
||||
|
||||
} cmsViewingConditions, FAR* LPcmsViewingConditions;
|
||||
|
||||
|
||||
|
||||
LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC);
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel);
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut);
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut);
|
||||
|
||||
*/
|
||||
|
||||
// ---------- Implementation --------------------------------------------
|
||||
|
||||
// #define USE_CIECAM97s2 1
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
|
||||
# define NOISE_CONSTANT 3.05
|
||||
#else
|
||||
# define NOISE_CONSTANT 2.05
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
The model input data are the adapting field luminance in cd/m2
|
||||
(normally taken to be 20% of the luminance of white in the adapting field),
|
||||
LA , the relative tristimulus values of the stimulus, XYZ, the relative
|
||||
tristimulus values of white in the same viewing conditions, Xw Yw Zw ,
|
||||
and the relative luminance of the background, Yb . Relative tristimulus
|
||||
values should be expressed on a scale from Y = 0 for a perfect black
|
||||
to Y = 100 for a perfect reflecting diffuser. Additionally, the
|
||||
parameters c, for the impact of surround, Nc , a chromatic induction factor,
|
||||
and F, a factor for degree of adaptation, must be selected according to the
|
||||
guidelines in table
|
||||
|
||||
All CIE tristimulus values are obtained using the CIE 1931
|
||||
Standard Colorimetric Observer (2°).
|
||||
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
|
||||
cmsCIEXYZ WP;
|
||||
int surround;
|
||||
int calculate_D;
|
||||
|
||||
double Yb; // rel. luminance of background
|
||||
|
||||
cmsCIEXYZ RefWhite;
|
||||
|
||||
double La; // The adapting field luminance in cd/m2
|
||||
|
||||
double c; // Impact of surround
|
||||
double Nc; // Chromatic induction factor
|
||||
double Fll; // Lightness contrast factor (Removed on rev 2)
|
||||
double F; // Degree of adaptation
|
||||
|
||||
|
||||
double k;
|
||||
double Fl;
|
||||
|
||||
double Nbb; // The background and chromatic brightness induction factors.
|
||||
double Ncb;
|
||||
double z; // base exponential nonlinearity
|
||||
double n; // background induction factor
|
||||
double D;
|
||||
|
||||
MAT3 MlamRigg;
|
||||
MAT3 MlamRigg_1;
|
||||
|
||||
MAT3 Mhunt;
|
||||
MAT3 Mhunt_1;
|
||||
|
||||
MAT3 Mhunt_x_MlamRigg_1;
|
||||
MAT3 MlamRigg_x_Mhunt_1;
|
||||
|
||||
|
||||
VEC3 RGB_subw;
|
||||
VEC3 RGB_subw_prime;
|
||||
|
||||
double p;
|
||||
|
||||
VEC3 RGB_subwc;
|
||||
|
||||
VEC3 RGB_subaw_prime;
|
||||
double A_subw;
|
||||
double Q_subw;
|
||||
|
||||
} cmsCIECAM97s,FAR *LPcmsCIECAM97s;
|
||||
|
||||
|
||||
|
||||
// Free model structure
|
||||
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel)
|
||||
{
|
||||
LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
|
||||
if (lpMod) free(lpMod);
|
||||
}
|
||||
|
||||
// Partial discounting for adaptation degree computation
|
||||
|
||||
static
|
||||
double discount(double d, double chan)
|
||||
{
|
||||
return (d * chan + 1 - d);
|
||||
}
|
||||
|
||||
|
||||
// This routine does model exponential nonlinearity on the short wavelenght
|
||||
// sensitive channel. On CIECAM97s rev 2 this has been reverted to linear.
|
||||
|
||||
static
|
||||
void FwAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB)
|
||||
{
|
||||
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]);
|
||||
RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]);
|
||||
RGBc->n[2] = RGB->n[2]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]);
|
||||
#else
|
||||
|
||||
RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]);
|
||||
RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]);
|
||||
|
||||
RGBc->n[2] = pow(fabs(RGB->n[2]), lpMod ->p) * discount(lpMod->D, (1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)));
|
||||
|
||||
// If B happens to be negative, Then Bc is also set to be negative
|
||||
|
||||
if (RGB->n[2] < 0)
|
||||
RGBc->n[2] = -RGBc->n[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void RvAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB)
|
||||
{
|
||||
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]);
|
||||
RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]);
|
||||
RGBc->n[2] = RGB->n[2]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]);
|
||||
#else
|
||||
|
||||
RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]);
|
||||
RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]);
|
||||
RGBc->n[2] = pow(fabs(RGB->n[2]), 1.0/lpMod->p)/pow(discount(lpMod->D, 1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)), 1.0/lpMod->p);
|
||||
if (RGB->n[2] < 0)
|
||||
RGBc->n[2] = -RGBc->n[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
void PostAdaptationConeResponses(LPcmsCIECAM97s lpMod, LPVEC3 RGBa_prime, LPVEC3 RGBprime)
|
||||
{
|
||||
if (RGBprime->n[0]>=0.0) {
|
||||
|
||||
RGBa_prime->n[0]=((40.0*pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73)+2))+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RGBa_prime->n[0]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73)+2))+1;
|
||||
}
|
||||
|
||||
if (RGBprime->n[1]>=0.0)
|
||||
{
|
||||
RGBa_prime->n[1]=((40.0*pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73)+2))+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RGBa_prime->n[1]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73)+2))+1;
|
||||
}
|
||||
|
||||
if (RGBprime->n[2]>=0.0)
|
||||
{
|
||||
RGBa_prime->n[2]=((40.0*pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73)+2))+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RGBa_prime->n[2]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73)+2))+1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute hue quadrature, eccentricity factor, e
|
||||
|
||||
static
|
||||
void ComputeHueQuadrature(double h, double* H, double* e)
|
||||
{
|
||||
|
||||
|
||||
#define IRED 0
|
||||
#define IYELLOW 1
|
||||
#define IGREEN 2
|
||||
#define IBLUE 3
|
||||
|
||||
double e_tab[] = {0.8, 0.7, 1.0, 1.2};
|
||||
double H_tab[] = { 0, 100, 200, 300};
|
||||
int p1, p2;
|
||||
double e1, e2, h1, h2;
|
||||
|
||||
|
||||
if (h >= 20.14 && h < 90.0) { // Red
|
||||
|
||||
p1 = IRED;
|
||||
p2 = IYELLOW;
|
||||
}
|
||||
else
|
||||
if (h >= 90.0 && h < 164.25) { // Yellow
|
||||
|
||||
p1 = IYELLOW;
|
||||
p2 = IGREEN;
|
||||
}
|
||||
else
|
||||
if (h >= 164.25 && h < 237.53) { // Green
|
||||
|
||||
p1 = IGREEN;
|
||||
p2 = IBLUE; }
|
||||
else { // Blue
|
||||
|
||||
p1 = IBLUE;
|
||||
p2 = IRED;
|
||||
}
|
||||
|
||||
e1 = e_tab[p1]; e2 = e_tab[p2];
|
||||
h1 = H_tab[p1]; h2 = H_tab[p2];
|
||||
|
||||
|
||||
|
||||
*e = e1 + ((e2-e1)*(h-h1)/(h2 - h1));
|
||||
*H = h1 + (100. * (h - h1) / e1) / ((h - h1)/e1 + (h2 - h) / e2);
|
||||
|
||||
#undef IRED
|
||||
#undef IYELLOW
|
||||
#undef IGREEN
|
||||
#undef IBLUE
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC)
|
||||
{
|
||||
LPcmsCIECAM97s lpMod;
|
||||
VEC3 tmp;
|
||||
|
||||
if((lpMod = (LPcmsCIECAM97s) _cmsMalloc(sizeof(cmsCIECAM97s))) == NULL) {
|
||||
return (LCMSHANDLE) NULL;
|
||||
}
|
||||
|
||||
|
||||
lpMod->WP.X = pVC->whitePoint.X;
|
||||
lpMod->WP.Y = pVC->whitePoint.Y;
|
||||
lpMod->WP.Z = pVC->whitePoint.Z;
|
||||
|
||||
lpMod->Yb = pVC->Yb;
|
||||
lpMod->La = pVC->La;
|
||||
|
||||
lpMod->surround = pVC->surround;
|
||||
|
||||
lpMod->RefWhite.X = 100.0;
|
||||
lpMod->RefWhite.Y = 100.0;
|
||||
lpMod->RefWhite.Z = 100.0;
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
|
||||
VEC3init(&lpMod->MlamRigg.v[0], 0.8562, 0.3372, -0.1934);
|
||||
VEC3init(&lpMod->MlamRigg.v[1], -0.8360, 1.8327, 0.0033);
|
||||
VEC3init(&lpMod->MlamRigg.v[2], 0.0357,-0.0469, 1.0112);
|
||||
|
||||
VEC3init(&lpMod->MlamRigg_1.v[0], 0.9874, -0.1768, 0.1894);
|
||||
VEC3init(&lpMod->MlamRigg_1.v[1], 0.4504, 0.4649, 0.0846);
|
||||
VEC3init(&lpMod->MlamRigg_1.v[2],-0.0139, 0.0278, 0.9861);
|
||||
|
||||
#else
|
||||
// Bradford transform: Lam-Rigg cone responses
|
||||
VEC3init(&lpMod->MlamRigg.v[0], 0.8951, 0.2664, -0.1614);
|
||||
VEC3init(&lpMod->MlamRigg.v[1], -0.7502, 1.7135, 0.0367);
|
||||
VEC3init(&lpMod->MlamRigg.v[2], 0.0389, -0.0685, 1.0296);
|
||||
|
||||
|
||||
// Inverse of Lam-Rigg
|
||||
VEC3init(&lpMod->MlamRigg_1.v[0], 0.98699, -0.14705, 0.15996);
|
||||
VEC3init(&lpMod->MlamRigg_1.v[1], 0.43231, 0.51836, 0.04929);
|
||||
VEC3init(&lpMod->MlamRigg_1.v[2], -0.00853, 0.04004, 0.96849);
|
||||
|
||||
#endif
|
||||
|
||||
// Hunt-Pointer-Estevez cone responses
|
||||
VEC3init(&lpMod->Mhunt.v[0], 0.38971, 0.68898, -0.07868);
|
||||
VEC3init(&lpMod->Mhunt.v[1], -0.22981, 1.18340, 0.04641);
|
||||
VEC3init(&lpMod->Mhunt.v[2], 0.0, 0.0, 1.0);
|
||||
|
||||
// Inverse of Hunt-Pointer-Estevez
|
||||
VEC3init(&lpMod->Mhunt_1.v[0], 1.91019, -1.11214, 0.20195);
|
||||
VEC3init(&lpMod->Mhunt_1.v[1], 0.37095, 0.62905, 0.0);
|
||||
VEC3init(&lpMod->Mhunt_1.v[2], 0.0, 0.0, 1.0);
|
||||
|
||||
|
||||
if (pVC->D_value == -1.0)
|
||||
lpMod->calculate_D = 1;
|
||||
else
|
||||
if (pVC->D_value == -2.0)
|
||||
lpMod->calculate_D = 2;
|
||||
else {
|
||||
lpMod->calculate_D = 0;
|
||||
lpMod->D = pVC->D_value;
|
||||
}
|
||||
|
||||
// Table I (revised)
|
||||
|
||||
switch (lpMod->surround) {
|
||||
|
||||
case AVG_SURROUND_4:
|
||||
lpMod->F = 1.0;
|
||||
lpMod->c = 0.69;
|
||||
lpMod->Fll = 0.0; // Not included on Rev 2
|
||||
lpMod->Nc = 1.0;
|
||||
break;
|
||||
case AVG_SURROUND:
|
||||
lpMod->F = 1.0;
|
||||
lpMod->c = 0.69;
|
||||
lpMod->Fll = 1.0;
|
||||
lpMod->Nc = 1.0;
|
||||
break;
|
||||
case DIM_SURROUND:
|
||||
lpMod->F = 0.99;
|
||||
lpMod->c = 0.59;
|
||||
lpMod->Fll = 1.0;
|
||||
lpMod->Nc = 0.95;
|
||||
break;
|
||||
case DARK_SURROUND:
|
||||
lpMod->F = 0.9;
|
||||
lpMod->c = 0.525;
|
||||
lpMod->Fll = 1.0;
|
||||
lpMod->Nc = 0.8;
|
||||
break;
|
||||
case CUTSHEET_SURROUND:
|
||||
lpMod->F = 0.9;
|
||||
lpMod->c = 0.41;
|
||||
lpMod->Fll = 1.0;
|
||||
lpMod->Nc = 0.8;
|
||||
break;
|
||||
default:
|
||||
lpMod->F = 1.0;
|
||||
lpMod->c = 0.69;
|
||||
lpMod->Fll = 1.0;
|
||||
lpMod->Nc = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
lpMod->k = 1 / (5 * lpMod->La + 1);
|
||||
lpMod->Fl = lpMod->La * pow(lpMod->k, 4) + 0.1*pow(1 - pow(lpMod->k, 4), 2.0) * pow(5*lpMod->La, 1.0/3.0);
|
||||
|
||||
if (lpMod->calculate_D > 0) {
|
||||
|
||||
lpMod->D = lpMod->F * (1 - 1 / (1 + 2*pow(lpMod->La, 0.25) + pow(lpMod->La, 2)/300.0));
|
||||
if (lpMod->calculate_D > 1)
|
||||
lpMod->D = (lpMod->D + 1.0) / 2;
|
||||
}
|
||||
|
||||
|
||||
// RGB_subw = [MlamRigg][WP/YWp]
|
||||
#ifdef USE_CIECAM97s2
|
||||
MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, (LPVEC3) &lpMod -> WP);
|
||||
#else
|
||||
VEC3divK(&tmp, (LPVEC3) &lpMod -> WP, lpMod->WP.Y);
|
||||
MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &tmp);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
MAT3per(&lpMod -> Mhunt_x_MlamRigg_1, &lpMod -> Mhunt, &lpMod->MlamRigg_1 );
|
||||
MAT3per(&lpMod -> MlamRigg_x_Mhunt_1, &lpMod -> MlamRigg, &lpMod -> Mhunt_1 );
|
||||
|
||||
// p is used on forward model
|
||||
lpMod->p = pow(lpMod->RGB_subw.n[2], 0.0834);
|
||||
|
||||
FwAdaptationDegree(lpMod, &lpMod->RGB_subwc, &lpMod->RGB_subw);
|
||||
|
||||
#if USE_CIECAM97s2
|
||||
MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &lpMod -> RGB_subwc);
|
||||
#else
|
||||
VEC3perK(&tmp, &lpMod -> RGB_subwc, lpMod->WP.Y);
|
||||
MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &tmp);
|
||||
#endif
|
||||
|
||||
lpMod->n = lpMod-> Yb / lpMod-> WP.Y;
|
||||
|
||||
lpMod->z = 1 + lpMod->Fll * sqrt(lpMod->n);
|
||||
lpMod->Nbb = lpMod->Ncb = 0.725 / pow(lpMod->n, 0.2);
|
||||
|
||||
PostAdaptationConeResponses(lpMod, &lpMod->RGB_subaw_prime, &lpMod->RGB_subw_prime);
|
||||
|
||||
lpMod->A_subw=lpMod->Nbb*(2.0*lpMod->RGB_subaw_prime.n[0]+lpMod->RGB_subaw_prime.n[1]+lpMod->RGB_subaw_prime.n[2]/20.0-NOISE_CONSTANT);
|
||||
|
||||
return (LCMSHANDLE) lpMod;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// The forward model: XYZ -> JCh
|
||||
//
|
||||
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ inPtr, LPcmsJCh outPtr)
|
||||
{
|
||||
|
||||
LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
|
||||
double a, b, h, s, H1val, es, A;
|
||||
VEC3 In, RGB, RGBc, RGBprime, RGBa_prime;
|
||||
|
||||
if (inPtr -> Y <= 0.0) {
|
||||
|
||||
outPtr -> J = outPtr -> C = outPtr -> h = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// An initial chromatic adaptation transform is used to go from the source
|
||||
// viewing conditions to corresponding colours under the equal-energy-illuminant
|
||||
// reference viewing conditions. This is handled differently on rev 2
|
||||
|
||||
VEC3init(&In, inPtr -> X, inPtr -> Y, inPtr -> Z); // 2.1
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
// Since the chromatic adaptation transform has been linearized, it
|
||||
// is no longer required to divide the stimulus tristimulus values
|
||||
// by their own Y tristimulus value prior to the chromatic adaptation.
|
||||
#else
|
||||
VEC3divK(&In, &In, inPtr -> Y);
|
||||
#endif
|
||||
|
||||
MAT3eval(&RGB, &lpMod -> MlamRigg, &In); // 2.2
|
||||
|
||||
FwAdaptationDegree(lpMod, &RGBc, &RGB);
|
||||
|
||||
// The post-adaptation signals for both the sample and the white are then
|
||||
// transformed from the sharpened cone responses to the Hunt-Pointer-Estevez
|
||||
// cone responses.
|
||||
#ifdef USE_CIECAM97s2
|
||||
#else
|
||||
VEC3perK(&RGBc, &RGBc, inPtr->Y);
|
||||
#endif
|
||||
|
||||
MAT3eval(&RGBprime, &lpMod->Mhunt_x_MlamRigg_1, &RGBc);
|
||||
|
||||
// The post-adaptation cone responses (for both the stimulus and the white)
|
||||
// are then calculated.
|
||||
|
||||
PostAdaptationConeResponses(lpMod, &RGBa_prime, &RGBprime);
|
||||
|
||||
// Preliminary red-green and yellow-blue opponent dimensions are calculated
|
||||
|
||||
a = RGBa_prime.n[0] - (12.0 * RGBa_prime.n[1] / 11.0) + RGBa_prime.n[2]/11.0;
|
||||
b = (RGBa_prime.n[0] + RGBa_prime.n[1] - 2.0 * RGBa_prime.n[2]) / 9.0;
|
||||
|
||||
|
||||
// The CIECAM97s hue angle, h, is then calculated
|
||||
h = (180.0/M_PI)*(atan2(b, a));
|
||||
|
||||
|
||||
while (h < 0)
|
||||
h += 360.0;
|
||||
|
||||
outPtr->h = h;
|
||||
|
||||
// hue quadrature and eccentricity factors, e, are calculated
|
||||
|
||||
ComputeHueQuadrature(h, &H1val, &es);
|
||||
|
||||
// ComputeHueQuadrature(h, &H1val, &h1, &e1, &h2, &e2, &es);
|
||||
|
||||
|
||||
// The achromatic response A
|
||||
A = lpMod->Nbb * (2.0 * RGBa_prime.n[0] + RGBa_prime.n[1] + RGBa_prime.n[2]/20.0 - NOISE_CONSTANT);
|
||||
|
||||
// CIECAM97s Lightness J
|
||||
outPtr -> J = 100.0 * pow(A / lpMod->A_subw, lpMod->c * lpMod->z);
|
||||
|
||||
// CIECAM97s saturation s
|
||||
s = (50 * hypot (a, b) * 100 * es * (10.0/13.0) * lpMod-> Nc * lpMod->Ncb) / (RGBa_prime.n[0] + RGBa_prime.n[1] + 1.05 * RGBa_prime.n[2]);
|
||||
|
||||
// CIECAM97s Chroma C
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
// Eq. 26 has been modified to allow accurate prediction of the Munsell chroma scales.
|
||||
outPtr->C = 0.7487 * pow(s, 0.973) * pow(outPtr->J/100.0, 0.945 * lpMod->n) * (1.64 - pow(0.29, lpMod->n));
|
||||
|
||||
#else
|
||||
outPtr->C = 2.44 * pow(s, 0.69) * pow(outPtr->J/100.0, 0.67 * lpMod->n) * (1.64 - pow(0.29, lpMod->n));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// The reverse model JCh -> XYZ
|
||||
//
|
||||
|
||||
|
||||
LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh inPtr, LPcmsCIEXYZ outPtr)
|
||||
{
|
||||
LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
|
||||
double J, C, h, A, H1val, es, s, a, b;
|
||||
double tan_h, sec_h;
|
||||
double R_suba_prime, G_suba_prime, B_suba_prime;
|
||||
double R_prime, G_prime, B_prime;
|
||||
double Y_subc, Y_prime, B_term;
|
||||
VEC3 tmp;
|
||||
VEC3 RGB_prime, RGB_subc_Y;
|
||||
VEC3 Y_over_Y_subc_RGB;
|
||||
VEC3 XYZ_primeprime_over_Y_subc;
|
||||
#ifdef USE_CIECAM92s2
|
||||
VEC3 RGBY;
|
||||
VEC3 Out;
|
||||
#endif
|
||||
|
||||
J = inPtr->J;
|
||||
h = inPtr->h;
|
||||
C = inPtr->C;
|
||||
|
||||
if (J <= 0) {
|
||||
|
||||
outPtr->X = 0.0;
|
||||
outPtr->Y = 0.0;
|
||||
outPtr->Z = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// (2) From J Obtain A
|
||||
|
||||
A = pow(J/100.0, 1/(lpMod->c * lpMod->z)) * lpMod->A_subw;
|
||||
|
||||
|
||||
// (3), (4), (5) Using H Determine h1, h2, e1, e2
|
||||
// e1 and h1 are the values of e and h for the unique hue having the
|
||||
// nearest lower valur of h and e2 and h2 are the values of e and h for
|
||||
// the unique hue having the nearest higher value of h.
|
||||
|
||||
|
||||
ComputeHueQuadrature(h, &H1val, &es);
|
||||
|
||||
// (7) Calculate s
|
||||
|
||||
s = pow(C / (2.44 * pow(J/100.0, 0.67*lpMod->n) * (1.64 - pow(0.29, lpMod->n))) , (1./0.69));
|
||||
|
||||
|
||||
// (8) Calculate a and b.
|
||||
// NOTE: sqrt(1 + tan^2) == sec(h)
|
||||
|
||||
tan_h = tan ((M_PI/180.)*(h));
|
||||
sec_h = sqrt(1 + tan_h * tan_h);
|
||||
|
||||
if ((h > 90) && (h < 270))
|
||||
sec_h = -sec_h;
|
||||
|
||||
a = s * ( A/lpMod->Nbb + NOISE_CONSTANT) / ( sec_h * 50000.0 * es * lpMod->Nc * lpMod->Ncb/ 13.0 +
|
||||
s * (11.0 / 23.0 + (108.0/23.0) * tan_h));
|
||||
|
||||
b = a * tan_h;
|
||||
|
||||
//(9) Calculate R'a G'a and B'a
|
||||
|
||||
R_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) + (41.0/61.0) * (11.0/23.0) * a + (288.0/61.0) / 23.0 * b;
|
||||
G_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (81.0/61.0) * (11.0/23.0) * a - (261.0/61.0) / 23.0 * b;
|
||||
B_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (20.0/61.0) * (11.0/23.0) * a - (20.0/61.0) * (315.0/23.0) * b;
|
||||
|
||||
// (10) Calculate R', G' and B'
|
||||
|
||||
if ((R_suba_prime - 1) < 0) {
|
||||
|
||||
R_prime = -100.0 * pow((2.0 - 2.0 * R_suba_prime) /
|
||||
(39.0 + R_suba_prime), 1.0/0.73);
|
||||
}
|
||||
else
|
||||
{
|
||||
R_prime = 100.0 * pow((2.0 * R_suba_prime - 2.0) /
|
||||
(41.0 - R_suba_prime), 1.0/0.73);
|
||||
}
|
||||
|
||||
if ((G_suba_prime - 1) < 0)
|
||||
{
|
||||
G_prime = -100.0 * pow((2.0 - 2.0 * G_suba_prime) /
|
||||
(39.0 + G_suba_prime), 1.0/0.73);
|
||||
}
|
||||
else
|
||||
{
|
||||
G_prime = 100.0 * pow((2.0 * G_suba_prime - 2.0) /
|
||||
(41.0 - G_suba_prime), 1.0/0.73);
|
||||
}
|
||||
|
||||
if ((B_suba_prime - 1) < 0)
|
||||
{
|
||||
B_prime = -100.0 * pow((2.0 - 2.0 * B_suba_prime) /
|
||||
(39.0 + B_suba_prime), 1.0/0.73);
|
||||
}
|
||||
else
|
||||
{
|
||||
B_prime = 100.0 * pow((2.0 * B_suba_prime - 2.0) /
|
||||
(41.0 - B_suba_prime), 1.0/0.73);
|
||||
}
|
||||
|
||||
|
||||
// (11) Calculate RcY, GcY and BcY
|
||||
|
||||
VEC3init(&RGB_prime, R_prime, G_prime, B_prime);
|
||||
VEC3divK(&tmp, &RGB_prime, lpMod -> Fl);
|
||||
|
||||
MAT3eval(&RGB_subc_Y, &lpMod->MlamRigg_x_Mhunt_1, &tmp);
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef USE_CIECAM97s2
|
||||
|
||||
// (12)
|
||||
|
||||
|
||||
RvAdaptationDegree(lpMod, &RGBY, &RGB_subc_Y);
|
||||
MAT3eval(&Out, &lpMod->MlamRigg_1, &RGBY);
|
||||
|
||||
outPtr -> X = Out.n[0];
|
||||
outPtr -> Y = Out.n[1];
|
||||
outPtr -> Z = Out.n[2];
|
||||
|
||||
#else
|
||||
|
||||
// (12) Calculate Yc
|
||||
|
||||
Y_subc = 0.43231*RGB_subc_Y.n[0]+0.51836*RGB_subc_Y.n[1]+0.04929*RGB_subc_Y.n[2];
|
||||
|
||||
// (13) Calculate (Y/Yc)R, (Y/Yc)G and (Y/Yc)B
|
||||
|
||||
VEC3divK(&RGB_subc_Y, &RGB_subc_Y, Y_subc);
|
||||
RvAdaptationDegree(lpMod, &Y_over_Y_subc_RGB, &RGB_subc_Y);
|
||||
|
||||
// (14) Calculate Y'
|
||||
Y_prime = 0.43231*(Y_over_Y_subc_RGB.n[0]*Y_subc) + 0.51836*(Y_over_Y_subc_RGB.n[1]*Y_subc) + 0.04929 * (Y_over_Y_subc_RGB.n[2]*Y_subc);
|
||||
|
||||
if (Y_prime < 0 || Y_subc < 0)
|
||||
{
|
||||
// Discard to near black point
|
||||
|
||||
outPtr -> X = 0;
|
||||
outPtr -> Y = 0;
|
||||
outPtr -> Z = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
B_term = pow(Y_prime / Y_subc, (1.0 / lpMod->p) - 1);
|
||||
|
||||
// (15) Calculate X'', Y'' and Z''
|
||||
Y_over_Y_subc_RGB.n[2] /= B_term;
|
||||
MAT3eval(&XYZ_primeprime_over_Y_subc, &lpMod->MlamRigg_1, &Y_over_Y_subc_RGB);
|
||||
|
||||
outPtr->X = XYZ_primeprime_over_Y_subc.n[0] * Y_subc;
|
||||
outPtr->Y = XYZ_primeprime_over_Y_subc.n[1] * Y_subc;
|
||||
outPtr->Z = XYZ_primeprime_over_Y_subc.n[2] * Y_subc;
|
||||
#endif
|
||||
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,637 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
This module provides conversion stages for handling intents.
|
||||
|
||||
The chain of evaluation in a transform is:
|
||||
|
||||
PCS1 PCS2 PCS3 PCS4
|
||||
|
||||
|From | |From | |Conversion | |Preview | |Gamut | |Conversion | |To | |To |
|
||||
|Input|->|Device|->|Stage 1 |->|handling|->|Checking|->|Stage 2 |->|Device|->|output |
|
||||
|
||||
-------- ------- ------------- --------- ---------- ------------- ------- ---------
|
||||
|
||||
AToB0 prew0 gamut BToA0
|
||||
Formatting LUT Adjusting LUT LUT Adjusting LUT Formatting
|
||||
Intent Intent 1 intent intent Intent 2 Intent
|
||||
|
||||
|
||||
Some of these LUT may be missing
|
||||
|
||||
There are two intents involved here, the intent of the transform itself, and the
|
||||
intent the proof is being done, if is the case. Since the first intent is to be
|
||||
applied to preview, is the proofing intent. The second intent identifies the
|
||||
transform intent. Input data of any stage is taked as relative colorimetric
|
||||
always.
|
||||
|
||||
|
||||
NOTES: V4 states than perceptual & saturation intents between mixed v2 & v4 profiles should
|
||||
scale PCS from a black point equal to ZERO in v2 profiles to the reference media black of
|
||||
perceptual v4 PCS. Since I found many v2 profiles to be using a perceptual intent with black
|
||||
point not zero at all, I'm implementing that as a black point compensation from whatever
|
||||
black from perceptal intent to the reference media black for v4 profiles.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
int cdecl cmsChooseCnvrt(int Absolute,
|
||||
int Phase1, LPcmsCIEXYZ BlackPointIn,
|
||||
LPcmsCIEXYZ WhitePointIn,
|
||||
LPcmsCIEXYZ IlluminantIn,
|
||||
LPMAT3 ChromaticAdaptationMatrixIn,
|
||||
|
||||
int Phase2, LPcmsCIEXYZ BlackPointOut,
|
||||
LPcmsCIEXYZ WhitePointOut,
|
||||
LPcmsCIEXYZ IlluminantOut,
|
||||
LPMAT3 ChromaticAdaptationMatrixOut,
|
||||
|
||||
int DoBlackPointCompensation,
|
||||
double AdaptationState,
|
||||
_cmsADJFN *fn1,
|
||||
LPWMAT3 wm, LPWVEC3 wof);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// D50 - Widely used
|
||||
|
||||
LCMSAPI LPcmsCIEXYZ LCMSEXPORT cmsD50_XYZ(void)
|
||||
{
|
||||
static cmsCIEXYZ D50XYZ = {D50X, D50Y, D50Z};
|
||||
|
||||
return &D50XYZ;
|
||||
}
|
||||
|
||||
LCMSAPI LPcmsCIExyY LCMSEXPORT cmsD50_xyY(void)
|
||||
{
|
||||
static cmsCIExyY D50xyY;
|
||||
cmsXYZ2xyY(&D50xyY, cmsD50_XYZ());
|
||||
|
||||
return &D50xyY;
|
||||
}
|
||||
|
||||
|
||||
// ---------------- From LUT to LUT --------------------------
|
||||
|
||||
|
||||
// Calculate m, offset Relativ -> Absolute undoing any chromatic
|
||||
// adaptation done by the profile.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4100 4505)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// join scalings to obtain:
|
||||
// relative input to absolute and then to relative output
|
||||
|
||||
static
|
||||
void Rel2RelStepAbsCoefs(double AdaptationState,
|
||||
|
||||
LPcmsCIEXYZ BlackPointIn,
|
||||
LPcmsCIEXYZ WhitePointIn,
|
||||
LPcmsCIEXYZ IlluminantIn,
|
||||
LPMAT3 ChromaticAdaptationMatrixIn,
|
||||
|
||||
LPcmsCIEXYZ BlackPointOut,
|
||||
LPcmsCIEXYZ WhitePointOut,
|
||||
LPcmsCIEXYZ IlluminantOut,
|
||||
LPMAT3 ChromaticAdaptationMatrixOut,
|
||||
|
||||
LPMAT3 m, LPVEC3 of)
|
||||
{
|
||||
|
||||
VEC3 WtPtIn, WtPtInAdapted;
|
||||
VEC3 WtPtOut, WtPtOutAdapted;
|
||||
MAT3 Scale, m1, m2, m3;
|
||||
|
||||
VEC3init(&WtPtIn, WhitePointIn->X, WhitePointIn->Y, WhitePointIn->Z);
|
||||
MAT3eval(&WtPtInAdapted, ChromaticAdaptationMatrixIn, &WtPtIn);
|
||||
|
||||
VEC3init(&WtPtOut, WhitePointOut->X, WhitePointOut->Y, WhitePointOut->Z);
|
||||
MAT3eval(&WtPtOutAdapted, ChromaticAdaptationMatrixOut, &WtPtOut);
|
||||
|
||||
VEC3init(&Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0);
|
||||
VEC3init(&Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0);
|
||||
VEC3init(&Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]);
|
||||
|
||||
|
||||
// Adaptation state
|
||||
|
||||
if (AdaptationState == 1.0) {
|
||||
|
||||
// Observer is fully adapted. Keep chromatic adaptation
|
||||
|
||||
CopyMemory(m, &Scale, sizeof(MAT3));
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// Observer is not adapted, undo the chromatic adaptation
|
||||
m1 = *ChromaticAdaptationMatrixIn;
|
||||
MAT3inverse(&m1, &m2);
|
||||
|
||||
MAT3per(&m3, &m2, &Scale);
|
||||
MAT3per(m, &m3, ChromaticAdaptationMatrixOut);
|
||||
}
|
||||
|
||||
|
||||
VEC3init(of, 0.0, 0.0, 0.0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// The (in)famous black point compensation. Right now implemented as
|
||||
// a linear scaling in XYZ
|
||||
|
||||
static
|
||||
void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn,
|
||||
LPcmsCIEXYZ WhitePointIn,
|
||||
LPcmsCIEXYZ IlluminantIn,
|
||||
LPcmsCIEXYZ BlackPointOut,
|
||||
LPcmsCIEXYZ WhitePointOut,
|
||||
LPcmsCIEXYZ IlluminantOut,
|
||||
LPMAT3 m, LPVEC3 of)
|
||||
{
|
||||
|
||||
|
||||
cmsCIEXYZ RelativeBlackPointIn, RelativeBlackPointOut;
|
||||
double ax, ay, az, bx, by, bz, tx, ty, tz;
|
||||
|
||||
// At first, convert both black points to relative.
|
||||
|
||||
cmsAdaptToIlluminant(&RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn);
|
||||
cmsAdaptToIlluminant(&RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut);
|
||||
|
||||
// Now we need to compute a matrix plus an offset m and of such of
|
||||
// [m]*bpin + off = bpout
|
||||
// [m]*D50 + off = D50
|
||||
//
|
||||
// This is a linear scaling in the form ax+b, where
|
||||
// a = (bpout - D50) / (bpin - D50)
|
||||
// b = - D50* (bpout - bpin) / (bpin - D50)
|
||||
|
||||
|
||||
tx = RelativeBlackPointIn.X - IlluminantIn ->X;
|
||||
ty = RelativeBlackPointIn.Y - IlluminantIn ->Y;
|
||||
tz = RelativeBlackPointIn.Z - IlluminantIn ->Z;
|
||||
|
||||
ax = (RelativeBlackPointOut.X - IlluminantOut ->X) / tx;
|
||||
ay = (RelativeBlackPointOut.Y - IlluminantOut ->Y) / ty;
|
||||
az = (RelativeBlackPointOut.Z - IlluminantOut ->Z) / tz;
|
||||
|
||||
bx = - IlluminantOut -> X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx;
|
||||
by = - IlluminantOut -> Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty;
|
||||
bz = - IlluminantOut -> Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz;
|
||||
|
||||
|
||||
MAT3identity(m);
|
||||
|
||||
m->v[VX].n[0] = ax;
|
||||
m->v[VY].n[1] = ay;
|
||||
m->v[VZ].n[2] = az;
|
||||
|
||||
VEC3init(of, bx, by, bz);
|
||||
|
||||
}
|
||||
|
||||
// Return TRUE if both m and of are empy -- "m" being identity and "of" being 0
|
||||
|
||||
static
|
||||
LCMSBOOL IdentityParameters(LPWMAT3 m, LPWVEC3 of)
|
||||
{
|
||||
WVEC3 wv0;
|
||||
|
||||
VEC3initF(&wv0, 0, 0, 0);
|
||||
|
||||
if (!MAT3isIdentity(m, 0.00001)) return FALSE;
|
||||
if (!VEC3equal(of, &wv0, 0.00001)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------- Inter PCS conversions
|
||||
|
||||
// XYZ to XYZ linear scaling. Aso used on Black point compensation
|
||||
|
||||
static
|
||||
void XYZ2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
|
||||
{
|
||||
|
||||
WVEC3 a, r;
|
||||
|
||||
a.n[0] = In[0] << 1;
|
||||
a.n[1] = In[1] << 1;
|
||||
a.n[2] = In[2] << 1;
|
||||
|
||||
MAT3evalW(&r, m, &a);
|
||||
|
||||
Out[0] = _cmsClampWord((r.n[VX] + of->n[VX]) >> 1);
|
||||
Out[1] = _cmsClampWord((r.n[VY] + of->n[VY]) >> 1);
|
||||
Out[2] = _cmsClampWord((r.n[VZ] + of->n[VZ]) >> 1);
|
||||
}
|
||||
|
||||
|
||||
// XYZ to Lab, scaling first
|
||||
|
||||
static
|
||||
void XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
|
||||
{
|
||||
WORD XYZ[3];
|
||||
|
||||
XYZ2XYZ(In, XYZ, m, of);
|
||||
cmsXYZ2LabEncoded(XYZ, Out);
|
||||
}
|
||||
|
||||
// Lab to XYZ, then scalling
|
||||
|
||||
static
|
||||
void Lab2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
|
||||
{
|
||||
WORD XYZ[3];
|
||||
|
||||
cmsLab2XYZEncoded(In, XYZ);
|
||||
XYZ2XYZ(XYZ, Out, m, of);
|
||||
}
|
||||
|
||||
// Lab to XYZ, scalling and then, back to Lab
|
||||
|
||||
static
|
||||
void Lab2XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
|
||||
{
|
||||
WORD XYZ[3], XYZ2[3];
|
||||
|
||||
cmsLab2XYZEncoded(In, XYZ);
|
||||
XYZ2XYZ(XYZ, XYZ2, m, of);
|
||||
cmsXYZ2LabEncoded(XYZ2, Out);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
// Dispatcher for XYZ Relative LUT
|
||||
|
||||
static
|
||||
int FromXYZRelLUT(int Absolute,
|
||||
LPcmsCIEXYZ BlackPointIn,
|
||||
LPcmsCIEXYZ WhitePointIn,
|
||||
LPcmsCIEXYZ IlluminantIn,
|
||||
LPMAT3 ChromaticAdaptationMatrixIn,
|
||||
|
||||
int Phase2, LPcmsCIEXYZ BlackPointOut,
|
||||
LPcmsCIEXYZ WhitePointOut,
|
||||
LPcmsCIEXYZ IlluminantOut,
|
||||
LPMAT3 ChromaticAdaptationMatrixOut,
|
||||
|
||||
int DoBlackPointCompensation,
|
||||
double AdaptationState,
|
||||
_cmsADJFN *fn1,
|
||||
LPMAT3 m, LPVEC3 of)
|
||||
|
||||
{
|
||||
switch (Phase2) {
|
||||
|
||||
// From relative XYZ to Relative XYZ.
|
||||
|
||||
case XYZRel:
|
||||
|
||||
if (Absolute)
|
||||
{
|
||||
// From input relative to absolute, and then
|
||||
// back to output relative
|
||||
|
||||
Rel2RelStepAbsCoefs(AdaptationState,
|
||||
BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
ChromaticAdaptationMatrixIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
ChromaticAdaptationMatrixOut,
|
||||
m, of);
|
||||
*fn1 = XYZ2XYZ;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// XYZ Relative to XYZ relative, no op required
|
||||
*fn1 = NULL;
|
||||
if (DoBlackPointCompensation) {
|
||||
|
||||
*fn1 = XYZ2XYZ;
|
||||
ComputeBlackPointCompensationFactors(BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
m, of);
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// From relative XYZ to Relative Lab
|
||||
|
||||
case LabRel:
|
||||
|
||||
// First pass XYZ to absolute, then to relative and
|
||||
// finally to Lab. I use here D50 for output in order
|
||||
// to prepare the "to Lab" conversion.
|
||||
|
||||
if (Absolute)
|
||||
{
|
||||
|
||||
Rel2RelStepAbsCoefs(AdaptationState,
|
||||
BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
ChromaticAdaptationMatrixIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
ChromaticAdaptationMatrixOut,
|
||||
m, of);
|
||||
|
||||
*fn1 = XYZ2Lab;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just Convert to Lab
|
||||
|
||||
MAT3identity(m);
|
||||
VEC3init(of, 0, 0, 0);
|
||||
*fn1 = XYZ2Lab;
|
||||
|
||||
if (DoBlackPointCompensation) {
|
||||
|
||||
ComputeBlackPointCompensationFactors(BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
m, of);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default: return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// From Lab Relative type LUT
|
||||
|
||||
static
|
||||
int FromLabRelLUT(int Absolute,
|
||||
LPcmsCIEXYZ BlackPointIn,
|
||||
LPcmsCIEXYZ WhitePointIn,
|
||||
LPcmsCIEXYZ IlluminantIn,
|
||||
LPMAT3 ChromaticAdaptationMatrixIn,
|
||||
|
||||
int Phase2, LPcmsCIEXYZ BlackPointOut,
|
||||
LPcmsCIEXYZ WhitePointOut,
|
||||
LPcmsCIEXYZ IlluminantOut,
|
||||
LPMAT3 ChromaticAdaptationMatrixOut,
|
||||
|
||||
int DoBlackPointCompensation,
|
||||
double AdaptationState,
|
||||
|
||||
_cmsADJFN *fn1,
|
||||
LPMAT3 m, LPVEC3 of)
|
||||
{
|
||||
|
||||
switch (Phase2) {
|
||||
|
||||
// From Lab Relative to XYZ Relative, very usual case
|
||||
|
||||
case XYZRel:
|
||||
|
||||
if (Absolute) { // Absolute intent
|
||||
|
||||
// From lab relative, to XYZ absolute, and then,
|
||||
// back to XYZ relative
|
||||
|
||||
Rel2RelStepAbsCoefs(AdaptationState,
|
||||
BlackPointIn,
|
||||
WhitePointIn,
|
||||
cmsD50_XYZ(),
|
||||
ChromaticAdaptationMatrixIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
ChromaticAdaptationMatrixOut,
|
||||
m, of);
|
||||
|
||||
*fn1 = Lab2XYZ;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// From Lab relative, to XYZ relative.
|
||||
|
||||
*fn1 = Lab2XYZ;
|
||||
if (DoBlackPointCompensation) {
|
||||
|
||||
ComputeBlackPointCompensationFactors(BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
m, of);
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case LabRel:
|
||||
|
||||
if (Absolute) {
|
||||
|
||||
// First pass to XYZ using the input illuminant
|
||||
// * InIlluminant / D50, then to absolute. Then
|
||||
// to relative, but for input
|
||||
|
||||
Rel2RelStepAbsCoefs(AdaptationState,
|
||||
BlackPointIn,
|
||||
WhitePointIn, IlluminantIn,
|
||||
ChromaticAdaptationMatrixIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut, cmsD50_XYZ(),
|
||||
ChromaticAdaptationMatrixOut,
|
||||
m, of);
|
||||
*fn1 = Lab2XYZ2Lab;
|
||||
}
|
||||
else
|
||||
{ // Lab -> Lab relative don't need any adjust unless
|
||||
// black point compensation
|
||||
|
||||
*fn1 = NULL;
|
||||
if (DoBlackPointCompensation) {
|
||||
|
||||
*fn1 = Lab2XYZ2Lab;
|
||||
ComputeBlackPointCompensationFactors(BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
m, of);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default: return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// This function does calculate the necessary conversion operations
|
||||
// needed from transpassing data from a LUT to a LUT. The conversion
|
||||
// is modeled as a pointer of function and two coefficients, a and b
|
||||
// The function is actually called only if not null pointer is provided,
|
||||
// and the two paramaters are passed in. There are several types of
|
||||
// conversions, but basically they do a linear scalling and a interchange
|
||||
|
||||
|
||||
|
||||
// Main dispatcher
|
||||
|
||||
int cmsChooseCnvrt(int Absolute,
|
||||
int Phase1, LPcmsCIEXYZ BlackPointIn,
|
||||
LPcmsCIEXYZ WhitePointIn,
|
||||
LPcmsCIEXYZ IlluminantIn,
|
||||
LPMAT3 ChromaticAdaptationMatrixIn,
|
||||
|
||||
int Phase2, LPcmsCIEXYZ BlackPointOut,
|
||||
LPcmsCIEXYZ WhitePointOut,
|
||||
LPcmsCIEXYZ IlluminantOut,
|
||||
LPMAT3 ChromaticAdaptationMatrixOut,
|
||||
|
||||
int DoBlackPointCompensation,
|
||||
double AdaptationState,
|
||||
_cmsADJFN *fn1,
|
||||
LPWMAT3 wm, LPWVEC3 wof)
|
||||
{
|
||||
|
||||
int rc;
|
||||
MAT3 m;
|
||||
VEC3 of;
|
||||
|
||||
|
||||
MAT3identity(&m);
|
||||
VEC3init(&of, 0, 0, 0);
|
||||
|
||||
switch (Phase1) {
|
||||
|
||||
// Input LUT is giving XYZ relative values.
|
||||
|
||||
case XYZRel: rc = FromXYZRelLUT(Absolute,
|
||||
BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
ChromaticAdaptationMatrixIn,
|
||||
Phase2,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
ChromaticAdaptationMatrixOut,
|
||||
DoBlackPointCompensation,
|
||||
AdaptationState,
|
||||
fn1, &m, &of);
|
||||
break;
|
||||
|
||||
|
||||
|
||||
// Input LUT is giving Lab relative values
|
||||
|
||||
case LabRel: rc = FromLabRelLUT(Absolute,
|
||||
BlackPointIn,
|
||||
WhitePointIn,
|
||||
IlluminantIn,
|
||||
ChromaticAdaptationMatrixIn,
|
||||
Phase2,
|
||||
BlackPointOut,
|
||||
WhitePointOut,
|
||||
IlluminantOut,
|
||||
ChromaticAdaptationMatrixOut,
|
||||
DoBlackPointCompensation,
|
||||
AdaptationState,
|
||||
fn1, &m, &of);
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
// Unrecognized combination
|
||||
|
||||
default: cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error");
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
MAT3toFix(wm, &m);
|
||||
VEC3toFix(wof, &of);
|
||||
|
||||
// Do some optimization -- discard conversion if identity parameters.
|
||||
|
||||
if (*fn1 == XYZ2XYZ || *fn1 == Lab2XYZ2Lab) {
|
||||
|
||||
if (IdentityParameters(wm, wof))
|
||||
*fn1 = NULL;
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
// As a rule, only the functions visible from API can signal
|
||||
// errors.
|
||||
|
||||
void cdecl cmsSignalError(int ErrorCode, const char *ErrorText, ...);
|
||||
|
||||
int LCMSEXPORT cmsErrorAction(int lAbort);
|
||||
void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn);
|
||||
|
||||
|
||||
// ******************************************************************
|
||||
|
||||
static int nDoAbort = LCMS_ERROR_ABORT;
|
||||
static cmsErrorHandlerFunction UserErrorHandler = (cmsErrorHandlerFunction) NULL;
|
||||
|
||||
|
||||
int LCMSEXPORT cmsErrorAction(int nAction)
|
||||
{
|
||||
int nOld = nDoAbort;
|
||||
nDoAbort = nAction;
|
||||
|
||||
return nOld;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn)
|
||||
{
|
||||
UserErrorHandler = Fn;
|
||||
}
|
||||
|
||||
|
||||
// Default error handler
|
||||
|
||||
|
||||
void cmsSignalError(int ErrorCode, const char *ErrorText, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (nDoAbort == LCMS_ERROR_IGNORE) return;
|
||||
|
||||
va_start(args, ErrorText);
|
||||
|
||||
if (UserErrorHandler != NULL) {
|
||||
|
||||
char Buffer[1024];
|
||||
|
||||
vsnprintf(Buffer, 1023, ErrorText, args);
|
||||
va_end(args);
|
||||
|
||||
if (UserErrorHandler(ErrorCode, Buffer)) {
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( __CONSOLE__ ) || defined( NON_WINDOWS )
|
||||
|
||||
fprintf(stderr, "lcms: Error #%d; ", ErrorCode);
|
||||
vfprintf(stderr, ErrorText, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
|
||||
if (nDoAbort == LCMS_ERROR_ABORT) exit(1);
|
||||
#else
|
||||
{
|
||||
char Buffer1[1024];
|
||||
char Buffer2[256];
|
||||
|
||||
snprintf(Buffer1, 767, "Error #%x; ", ErrorCode);
|
||||
vsnprintf(Buffer2, 255, ErrorText, args);
|
||||
strcat(Buffer1, Buffer2);
|
||||
MessageBox(NULL, Buffer1, "Little cms",
|
||||
MB_OK|MB_ICONSTOP|MB_TASKMODAL);
|
||||
va_end(args);
|
||||
|
||||
if (nDoAbort == LCMS_ERROR_ABORT) {
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
_cexit();
|
||||
#endif
|
||||
|
||||
FatalAppExit(0, "lcms is terminating application");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,954 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
// Gamma handling.
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries);
|
||||
void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma);
|
||||
void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]);
|
||||
LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma);
|
||||
LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src);
|
||||
LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma);
|
||||
LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]);
|
||||
LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma);
|
||||
LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints);
|
||||
LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda);
|
||||
|
||||
LCMSBOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nPoints);
|
||||
|
||||
|
||||
// Sampled curves
|
||||
|
||||
LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems);
|
||||
void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p);
|
||||
void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max);
|
||||
void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max);
|
||||
LCMSBOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda);
|
||||
void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints);
|
||||
|
||||
LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
|
||||
|
||||
double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t);
|
||||
double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold);
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#define MAX_KNOTS 4096
|
||||
typedef float vec[MAX_KNOTS+1];
|
||||
|
||||
|
||||
// Ciclic-redundant-check for assuring table is a true representation of parametric curve
|
||||
|
||||
// The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet
|
||||
#define QUOTIENT 0x04c11db7
|
||||
|
||||
static
|
||||
unsigned int Crc32(unsigned int result, LPVOID ptr, int len)
|
||||
{
|
||||
int i,j;
|
||||
BYTE octet;
|
||||
LPBYTE data = (LPBYTE) ptr;
|
||||
|
||||
for (i=0; i < len; i++) {
|
||||
|
||||
octet = *data++;
|
||||
|
||||
for (j=0; j < 8; j++) {
|
||||
|
||||
if (result & 0x80000000) {
|
||||
|
||||
result = (result << 1) ^ QUOTIENT ^ (octet >> 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (result << 1) ^ (octet >> 7);
|
||||
}
|
||||
octet <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get CRC of gamma table
|
||||
|
||||
unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table)
|
||||
{
|
||||
unsigned int crc = ~0U;
|
||||
|
||||
crc = Crc32(crc, &Table -> Seed.Type, sizeof(int));
|
||||
crc = Crc32(crc, Table ->Seed.Params, sizeof(double)*10);
|
||||
crc = Crc32(crc, &Table ->nEntries, sizeof(int));
|
||||
crc = Crc32(crc, Table ->GammaTable, sizeof(WORD) * Table -> nEntries);
|
||||
|
||||
return ~crc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries)
|
||||
{
|
||||
LPGAMMATABLE p;
|
||||
size_t size;
|
||||
|
||||
if (nEntries > 65530 || nEntries < 0) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't create gammatable of more than 65530 entries");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries-1));
|
||||
|
||||
p = (LPGAMMATABLE) _cmsMalloc(size);
|
||||
if (!p) return NULL;
|
||||
|
||||
ZeroMemory(p, size);
|
||||
|
||||
p -> Seed.Type = 0;
|
||||
p -> nEntries = nEntries;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma)
|
||||
{
|
||||
if (Gamma) free(Gamma);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3])
|
||||
{
|
||||
cmsFreeGamma(Gamma[0]);
|
||||
cmsFreeGamma(Gamma[1]);
|
||||
cmsFreeGamma(Gamma[2]);
|
||||
Gamma[0] = Gamma[1] = Gamma[2] = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Duplicate a gamma table
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE In)
|
||||
{
|
||||
LPGAMMATABLE Ptr;
|
||||
size_t size;
|
||||
|
||||
Ptr = cmsAllocGamma(In -> nEntries);
|
||||
if (Ptr == NULL) return NULL;
|
||||
|
||||
size = sizeof(GAMMATABLE) + (sizeof(WORD) * (In -> nEntries-1));
|
||||
|
||||
CopyMemory(Ptr, In, size);
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
|
||||
// Handle gamma using interpolation tables. The resulting curves can become
|
||||
// very stange, but are pleasent to eye.
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma,
|
||||
LPGAMMATABLE OutGamma)
|
||||
{
|
||||
register int i;
|
||||
L16PARAMS L16In, L16Out;
|
||||
LPWORD InPtr, OutPtr;
|
||||
LPGAMMATABLE p;
|
||||
|
||||
p = cmsAllocGamma(256);
|
||||
if (!p) return NULL;
|
||||
|
||||
cmsCalcL16Params(InGamma -> nEntries, &L16In);
|
||||
InPtr = InGamma -> GammaTable;
|
||||
|
||||
cmsCalcL16Params(OutGamma -> nEntries, &L16Out);
|
||||
OutPtr = OutGamma-> GammaTable;
|
||||
|
||||
for (i=0; i < 256; i++)
|
||||
{
|
||||
WORD wValIn, wValOut;
|
||||
|
||||
wValIn = cmsLinearInterpLUT16(RGB_8_TO_16(i), InPtr, &L16In);
|
||||
wValOut = cmsReverseLinearInterpLUT16(wValIn, OutPtr, &L16Out);
|
||||
|
||||
p -> GammaTable[i] = wValOut;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// New method, using smoothed parametric curves. This works FAR better.
|
||||
// We want to get
|
||||
//
|
||||
// y = f(g^-1(x)) ; f = ingamma, g = outgamma
|
||||
//
|
||||
// And this can be parametrized as
|
||||
//
|
||||
// y = f(t)
|
||||
// x = g(t)
|
||||
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma,
|
||||
LPGAMMATABLE OutGamma, int nPoints)
|
||||
{
|
||||
|
||||
LPSAMPLEDCURVE x, y, r;
|
||||
LPGAMMATABLE res;
|
||||
|
||||
x = cmsConvertGammaToSampledCurve(InGamma, nPoints);
|
||||
y = cmsConvertGammaToSampledCurve(OutGamma, nPoints);
|
||||
r = cmsJoinSampledCurves(y, x, nPoints);
|
||||
|
||||
// Does clean "hair"
|
||||
cmsSmoothSampledCurve(r, 0.001);
|
||||
|
||||
cmsClampSampledCurve(r, 0.0, 65535.0);
|
||||
|
||||
cmsFreeSampledCurve(x);
|
||||
cmsFreeSampledCurve(y);
|
||||
|
||||
res = cmsConvertSampledCurveToGamma(r, 65535.0);
|
||||
cmsFreeSampledCurve(r);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Reverse a gamma table
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma)
|
||||
{
|
||||
register int i;
|
||||
L16PARAMS L16In;
|
||||
LPWORD InPtr;
|
||||
LPGAMMATABLE p;
|
||||
|
||||
// Try to reverse it analytically whatever possible
|
||||
if (InGamma -> Seed.Type > 0 && InGamma -> Seed.Type <= 5 &&
|
||||
_cmsCrc32OfGammaTable(InGamma) == InGamma -> Seed.Crc32) {
|
||||
|
||||
return cmsBuildParametricGamma(nResultSamples, -(InGamma -> Seed.Type), InGamma ->Seed.Params);
|
||||
}
|
||||
|
||||
|
||||
// Nope, reverse the table
|
||||
p = cmsAllocGamma(nResultSamples);
|
||||
if (!p) return NULL;
|
||||
|
||||
cmsCalcL16Params(InGamma -> nEntries, &L16In);
|
||||
InPtr = InGamma -> GammaTable;
|
||||
|
||||
for (i=0; i < nResultSamples; i++)
|
||||
{
|
||||
WORD wValIn, wValOut;
|
||||
|
||||
wValIn = _cmsQuantizeVal(i, nResultSamples);
|
||||
wValOut = cmsReverseLinearInterpLUT16(wValIn, InPtr, &L16In);
|
||||
p -> GammaTable[i] = wValOut;
|
||||
}
|
||||
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Parametric curves
|
||||
//
|
||||
// Parameters goes as: Gamma, a, b, c, d, e, f
|
||||
// Type is the ICC type +1
|
||||
// if type is negative, then the curve is analyticaly inverted
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[])
|
||||
{
|
||||
LPGAMMATABLE Table;
|
||||
double R, Val, dval, e;
|
||||
int i;
|
||||
int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
|
||||
|
||||
Table = cmsAllocGamma(nEntries);
|
||||
if (NULL == Table) return NULL;
|
||||
|
||||
Table -> Seed.Type = Type;
|
||||
|
||||
CopyMemory(Table ->Seed.Params, Params, ParamsByType[abs(Type)] * sizeof(double));
|
||||
|
||||
|
||||
for (i=0; i < nEntries; i++) {
|
||||
|
||||
R = (double) i / (nEntries-1);
|
||||
|
||||
switch (Type) {
|
||||
|
||||
// X = Y ^ Gamma
|
||||
case 1:
|
||||
Val = pow(R, Params[0]);
|
||||
break;
|
||||
|
||||
// Type 1 Reversed: X = Y ^1/gamma
|
||||
case -1:
|
||||
Val = pow(R, 1/Params[0]);
|
||||
break;
|
||||
|
||||
// CIE 122-1966
|
||||
// Y = (aX + b)^Gamma | X >= -b/a
|
||||
// Y = 0 | else
|
||||
case 2:
|
||||
if (R >= -Params[2] / Params[1]) {
|
||||
|
||||
e = Params[1]*R + Params[2];
|
||||
|
||||
if (e > 0)
|
||||
Val = pow(e, Params[0]);
|
||||
else
|
||||
Val = 0;
|
||||
}
|
||||
else
|
||||
Val = 0;
|
||||
break;
|
||||
|
||||
// Type 2 Reversed
|
||||
// X = (Y ^1/g - b) / a
|
||||
case -2:
|
||||
|
||||
Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
|
||||
if (Val < 0)
|
||||
Val = 0;
|
||||
break;
|
||||
|
||||
|
||||
// IEC 61966-3
|
||||
// Y = (aX + b)^Gamma | X <= -b/a
|
||||
// Y = c | else
|
||||
case 3:
|
||||
if (R >= -Params[2] / Params[1]) {
|
||||
|
||||
e = Params[1]*R + Params[2];
|
||||
Val = pow(e, Params[0]) + Params[3];
|
||||
}
|
||||
else
|
||||
Val = Params[3];
|
||||
break;
|
||||
|
||||
|
||||
// Type 3 reversed
|
||||
// X=((Y-c)^1/g - b)/a | (Y>=c)
|
||||
// X=-b/a | (Y<c)
|
||||
|
||||
case -3:
|
||||
if (R >= Params[3]) {
|
||||
e = R - Params[3];
|
||||
Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1];
|
||||
if (Val < 0) Val = 0;
|
||||
}
|
||||
else {
|
||||
Val = -Params[2] / Params[1];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// IEC 61966-2.1 (sRGB)
|
||||
// Y = (aX + b)^Gamma | X >= d
|
||||
// Y = cX | X < d
|
||||
case 4:
|
||||
if (R >= Params[4]) {
|
||||
|
||||
e = Params[1]*R + Params[2];
|
||||
if (e > 0)
|
||||
Val = pow(e, Params[0]);
|
||||
else
|
||||
Val = 0;
|
||||
}
|
||||
else
|
||||
Val = R * Params[3];
|
||||
break;
|
||||
|
||||
// Type 4 reversed
|
||||
// X=((Y^1/g-b)/a) | Y >= (ad+b)^g
|
||||
// X=Y/c | Y< (ad+b)^g
|
||||
|
||||
case -4:
|
||||
if (R >= pow(Params[1] * Params[4] + Params[2], Params[0])) {
|
||||
|
||||
Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
|
||||
}
|
||||
else {
|
||||
Val = R / Params[3];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
// Y = (aX + b)^Gamma + e | X <= d
|
||||
// Y = cX + f | else
|
||||
case 5:
|
||||
if (R >= Params[4]) {
|
||||
|
||||
e = Params[1]*R + Params[2];
|
||||
Val = pow(e, Params[0]) + Params[5];
|
||||
}
|
||||
else
|
||||
Val = R*Params[3] + Params[6];
|
||||
break;
|
||||
|
||||
|
||||
// Reversed type 5
|
||||
// X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e)
|
||||
// X=(Y-f)/c | else
|
||||
case -5:
|
||||
|
||||
if (R >= pow(Params[1] * Params[4], Params[0]) + Params[5]) {
|
||||
|
||||
Val = pow(R - Params[5], 1/Params[0]) - Params[2] / Params[1];
|
||||
}
|
||||
else {
|
||||
Val = (R - Params[6]) / Params[3];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(Type)-1);
|
||||
cmsFreeGamma(Table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Saturate
|
||||
|
||||
dval = Val * 65535.0 + .5;
|
||||
if (dval > 65535.) dval = 65535.0;
|
||||
if (dval < 0) dval = 0;
|
||||
|
||||
Table->GammaTable[i] = (WORD) floor(dval);
|
||||
}
|
||||
|
||||
Table -> Seed.Crc32 = _cmsCrc32OfGammaTable(Table);
|
||||
|
||||
return Table;
|
||||
}
|
||||
|
||||
// Build a gamma table based on gamma constant
|
||||
|
||||
LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma)
|
||||
{
|
||||
return cmsBuildParametricGamma(nEntries, 1, &Gamma);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite
|
||||
// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.
|
||||
//
|
||||
// Smoothing and interpolation with second differences.
|
||||
//
|
||||
// Input: weights (w), data (y): vector from 1 to m.
|
||||
// Input: smoothing parameter (lambda), length (m).
|
||||
// Output: smoothed vector (z): vector from 1 to m.
|
||||
|
||||
|
||||
static
|
||||
void smooth2(vec w, vec y, vec z, float lambda, int m)
|
||||
{
|
||||
int i, i1, i2;
|
||||
vec c, d, e;
|
||||
d[1] = w[1] + lambda;
|
||||
c[1] = -2 * lambda / d[1];
|
||||
e[1] = lambda /d[1];
|
||||
z[1] = w[1] * y[1];
|
||||
d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1];
|
||||
c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2];
|
||||
e[2] = lambda / d[2];
|
||||
z[2] = w[2] * y[2] - c[1] * z[1];
|
||||
for (i = 3; i < m - 1; i++) {
|
||||
i1 = i - 1; i2 = i - 2;
|
||||
d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
|
||||
c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i];
|
||||
e[i] = lambda / d[i];
|
||||
z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2];
|
||||
}
|
||||
i1 = m - 2; i2 = m - 3;
|
||||
d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
|
||||
c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1];
|
||||
z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2];
|
||||
i1 = m - 1; i2 = m - 2;
|
||||
d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
|
||||
z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m];
|
||||
z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m];
|
||||
for (i = m - 2; 1<= i; i--)
|
||||
z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Smooths a curve sampled at regular intervals
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda)
|
||||
|
||||
{
|
||||
vec w, y, z;
|
||||
int i, nItems, Zeros, Poles;
|
||||
|
||||
|
||||
if (cmsIsLinear(Tab->GammaTable, Tab->nEntries)) return FALSE; // Nothing to do
|
||||
|
||||
nItems = Tab -> nEntries;
|
||||
|
||||
if (nItems > MAX_KNOTS) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothGamma: too many points.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ZeroMemory(w, nItems * sizeof(float));
|
||||
ZeroMemory(y, nItems * sizeof(float));
|
||||
ZeroMemory(z, nItems * sizeof(float));
|
||||
|
||||
for (i=0; i < nItems; i++)
|
||||
{
|
||||
y[i+1] = (float) Tab -> GammaTable[i];
|
||||
w[i+1] = 1.0;
|
||||
}
|
||||
|
||||
smooth2(w, y, z, (float) lambda, nItems);
|
||||
|
||||
// Do some reality - checking...
|
||||
Zeros = Poles = 0;
|
||||
for (i=nItems; i > 1; --i) {
|
||||
|
||||
if (z[i] == 0.) Zeros++;
|
||||
if (z[i] >= 65535.) Poles++;
|
||||
if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
|
||||
}
|
||||
|
||||
if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros
|
||||
if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles
|
||||
|
||||
// Seems ok
|
||||
|
||||
for (i=0; i < nItems; i++) {
|
||||
|
||||
// Clamp to WORD
|
||||
|
||||
float v = z[i+1];
|
||||
|
||||
if (v < 0) v = 0;
|
||||
if (v > 65535.) v = 65535.;
|
||||
|
||||
Tab -> GammaTable[i] = (WORD) floor(v + .5);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Check if curve is exponential, return gamma if so.
|
||||
|
||||
double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold)
|
||||
{
|
||||
double gamma, sum, sum2;
|
||||
double n, x, y, Std;
|
||||
int i;
|
||||
|
||||
sum = sum2 = n = 0;
|
||||
|
||||
// Does exclude endpoints
|
||||
for (i=1; i < nEntries - 1; i++) {
|
||||
|
||||
x = (double) i / (nEntries - 1);
|
||||
y = (double) GammaTable[i] / 65535.;
|
||||
|
||||
// Avoid 7% on lower part to prevent
|
||||
// artifacts due to linear ramps
|
||||
|
||||
if (y > 0. && y < 1. && x > 0.07) {
|
||||
|
||||
gamma = log(y) / log(x);
|
||||
sum += gamma;
|
||||
sum2 += gamma * gamma;
|
||||
n++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Take a look on SD to see if gamma isn't exponential at all
|
||||
Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
|
||||
|
||||
|
||||
if (Std > Thereshold)
|
||||
return -1.0;
|
||||
|
||||
return (sum / n); // The mean
|
||||
}
|
||||
|
||||
|
||||
double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t)
|
||||
{
|
||||
return cmsEstimateGammaEx(t->GammaTable, t->nEntries, 0.7);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------Sampled curves
|
||||
|
||||
// Allocate a empty curve
|
||||
|
||||
LPSAMPLEDCURVE cmsAllocSampledCurve(int nItems)
|
||||
{
|
||||
LPSAMPLEDCURVE pOut;
|
||||
|
||||
pOut = (LPSAMPLEDCURVE) _cmsMalloc(sizeof(SAMPLEDCURVE));
|
||||
if (pOut == NULL)
|
||||
return NULL;
|
||||
|
||||
if((pOut->Values = (double *) _cmsMalloc(nItems * sizeof(double))) == NULL)
|
||||
{
|
||||
free(pOut);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pOut->nItems = nItems;
|
||||
ZeroMemory(pOut->Values, nItems * sizeof(double));
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
|
||||
void cmsFreeSampledCurve(LPSAMPLEDCURVE p)
|
||||
{
|
||||
free((LPVOID) p -> Values);
|
||||
free((LPVOID) p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Does duplicate a sampled curve
|
||||
|
||||
LPSAMPLEDCURVE cmsDupSampledCurve(LPSAMPLEDCURVE p)
|
||||
{
|
||||
LPSAMPLEDCURVE out;
|
||||
|
||||
out = cmsAllocSampledCurve(p -> nItems);
|
||||
if (!out) return NULL;
|
||||
|
||||
CopyMemory(out ->Values, p ->Values, p->nItems * sizeof(double));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Take min, max of curve
|
||||
|
||||
void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max)
|
||||
{
|
||||
int i;
|
||||
|
||||
*Min = 65536.;
|
||||
*Max = 0.;
|
||||
|
||||
for (i=0; i < p -> nItems; i++) {
|
||||
|
||||
double v = p -> Values[i];
|
||||
|
||||
if (v < *Min)
|
||||
*Min = v;
|
||||
|
||||
if (v > *Max)
|
||||
*Max = v;
|
||||
}
|
||||
|
||||
if (*Min < 0) *Min = 0;
|
||||
if (*Max > 65535.0) *Max = 65535.0;
|
||||
}
|
||||
|
||||
// Clamps to Min, Max
|
||||
|
||||
void cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max)
|
||||
{
|
||||
|
||||
int i;
|
||||
|
||||
for (i=0; i < p -> nItems; i++) {
|
||||
|
||||
double v = p -> Values[i];
|
||||
|
||||
if (v < Min)
|
||||
v = Min;
|
||||
|
||||
if (v > Max)
|
||||
v = Max;
|
||||
|
||||
p -> Values[i] = v;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Smooths a curve sampled at regular intervals
|
||||
|
||||
LCMSBOOL cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double lambda)
|
||||
{
|
||||
vec w, y, z;
|
||||
int i, nItems;
|
||||
|
||||
nItems = Tab -> nItems;
|
||||
|
||||
if (nItems > MAX_KNOTS) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothSampledCurve: too many points.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ZeroMemory(w, nItems * sizeof(float));
|
||||
ZeroMemory(y, nItems * sizeof(float));
|
||||
ZeroMemory(z, nItems * sizeof(float));
|
||||
|
||||
for (i=0; i < nItems; i++)
|
||||
{
|
||||
float value = (float) Tab -> Values[i];
|
||||
|
||||
y[i+1] = value;
|
||||
w[i+1] = (float) ((value < 0.0) ? 0 : 1);
|
||||
}
|
||||
|
||||
|
||||
smooth2(w, y, z, (float) lambda, nItems);
|
||||
|
||||
for (i=0; i < nItems; i++) {
|
||||
|
||||
Tab -> Values[i] = z[i+1];;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Scale a value v, within domain Min .. Max
|
||||
// to a domain 0..(nPoints-1)
|
||||
|
||||
static
|
||||
double ScaleVal(double v, double Min, double Max, int nPoints)
|
||||
{
|
||||
|
||||
double a, b;
|
||||
|
||||
if (v <= Min) return 0;
|
||||
if (v >= Max) return (nPoints-1);
|
||||
|
||||
a = (double) (nPoints - 1) / (Max - Min);
|
||||
b = a * Min;
|
||||
|
||||
return (a * v) - b;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Does rescale a sampled curve to fit in a 0..(nPoints-1) domain
|
||||
|
||||
void cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints)
|
||||
{
|
||||
|
||||
int i;
|
||||
|
||||
for (i=0; i < p -> nItems; i++) {
|
||||
|
||||
double v = p -> Values[i];
|
||||
|
||||
p -> Values[i] = ScaleVal(v, Min, Max, nPoints);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Joins two sampled curves for X and Y. Curves should be sorted.
|
||||
|
||||
LPSAMPLEDCURVE cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints)
|
||||
{
|
||||
int i, j;
|
||||
LPSAMPLEDCURVE out;
|
||||
double MinX, MinY, MaxX, MaxY;
|
||||
double x, y, x1, y1, x2, y2, a, b;
|
||||
|
||||
out = cmsAllocSampledCurve(nResultingPoints);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
if (X -> nItems != Y -> nItems) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "cmsJoinSampledCurves: invalid curve.");
|
||||
cmsFreeSampledCurve(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get endpoints of sampled curves
|
||||
cmsEndpointsOfSampledCurve(X, &MinX, &MaxX);
|
||||
cmsEndpointsOfSampledCurve(Y, &MinY, &MaxY);
|
||||
|
||||
|
||||
// Set our points
|
||||
out ->Values[0] = MinY;
|
||||
for (i=1; i < nResultingPoints; i++) {
|
||||
|
||||
// Scale t to x domain
|
||||
x = (i * (MaxX - MinX) / (nResultingPoints-1)) + MinX;
|
||||
|
||||
// Find interval in which j is within (always up,
|
||||
// since fn should be monotonic at all)
|
||||
|
||||
j = 1;
|
||||
while ((j < X ->nItems - 1) && X ->Values[j] < x)
|
||||
j++;
|
||||
|
||||
// Now x is within X[j-1], X[j]
|
||||
x1 = X ->Values[j-1]; x2 = X ->Values[j];
|
||||
y1 = Y ->Values[j-1]; y2 = Y ->Values[j];
|
||||
|
||||
// Interpolate the value
|
||||
a = (y1 - y2) / (x1 - x2);
|
||||
b = y1 - a * x1;
|
||||
y = a* x + b;
|
||||
|
||||
out ->Values[i] = y;
|
||||
}
|
||||
|
||||
|
||||
cmsClampSampledCurve(out, MinY, MaxY);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Convert between curve types
|
||||
|
||||
LPGAMMATABLE cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max)
|
||||
{
|
||||
LPGAMMATABLE Gamma;
|
||||
int i, nPoints;
|
||||
|
||||
|
||||
nPoints = Sampled ->nItems;
|
||||
|
||||
Gamma = cmsAllocGamma(nPoints);
|
||||
for (i=0; i < nPoints; i++) {
|
||||
|
||||
Gamma->GammaTable[i] = (WORD) floor(ScaleVal(Sampled ->Values[i], 0, Max, 65536) + .5);
|
||||
}
|
||||
|
||||
return Gamma;
|
||||
|
||||
}
|
||||
|
||||
// Inverse of anterior
|
||||
|
||||
LPSAMPLEDCURVE cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints)
|
||||
{
|
||||
LPSAMPLEDCURVE Sampled;
|
||||
L16PARAMS L16;
|
||||
int i;
|
||||
WORD wQuant, wValIn;
|
||||
|
||||
if (nPoints > 4096) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "cmsConvertGammaToSampledCurve: too many points (max=4096)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmsCalcL16Params(Gamma -> nEntries, &L16);
|
||||
|
||||
Sampled = cmsAllocSampledCurve(nPoints);
|
||||
for (i=0; i < nPoints; i++) {
|
||||
wQuant = _cmsQuantizeVal(i, nPoints);
|
||||
wValIn = cmsLinearInterpLUT16(wQuant, Gamma ->GammaTable, &L16);
|
||||
Sampled ->Values[i] = (float) wValIn;
|
||||
}
|
||||
|
||||
return Sampled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Smooth endpoints (used in Black/White compensation)
|
||||
|
||||
LCMSBOOL _cmsSmoothEndpoints(LPWORD Table, int nEntries)
|
||||
{
|
||||
vec w, y, z;
|
||||
int i, Zeros, Poles;
|
||||
|
||||
|
||||
|
||||
if (cmsIsLinear(Table, nEntries)) return FALSE; // Nothing to do
|
||||
|
||||
|
||||
if (nEntries > MAX_KNOTS) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "_cmsSmoothEndpoints: too many points.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ZeroMemory(w, nEntries * sizeof(float));
|
||||
ZeroMemory(y, nEntries * sizeof(float));
|
||||
ZeroMemory(z, nEntries * sizeof(float));
|
||||
|
||||
for (i=0; i < nEntries; i++)
|
||||
{
|
||||
y[i+1] = (float) Table[i];
|
||||
w[i+1] = 1.0;
|
||||
}
|
||||
|
||||
w[1] = 65535.0;
|
||||
w[nEntries] = 65535.0;
|
||||
|
||||
smooth2(w, y, z, (float) nEntries, nEntries);
|
||||
|
||||
// Do some reality - checking...
|
||||
Zeros = Poles = 0;
|
||||
for (i=nEntries; i > 1; --i) {
|
||||
|
||||
if (z[i] == 0.) Zeros++;
|
||||
if (z[i] >= 65535.) Poles++;
|
||||
if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
|
||||
}
|
||||
|
||||
if (Zeros > (nEntries / 3)) return FALSE; // Degenerated, mostly zeros
|
||||
if (Poles > (nEntries / 3)) return FALSE; // Degenerated, mostly poles
|
||||
|
||||
// Seems ok
|
||||
|
||||
for (i=0; i < nEntries; i++) {
|
||||
|
||||
// Clamp to WORD
|
||||
|
||||
float v = z[i+1];
|
||||
|
||||
if (v < 0) v = 0;
|
||||
if (v > 65535.) v = 65535.;
|
||||
|
||||
Table[i] = (WORD) floor(v + .5);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,721 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
// Generic I/O, tag dictionary management, profile struct
|
||||
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
// Memory-based stream ---------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
LPBYTE Block; // Points to allocated memory
|
||||
size_t Size; // Size of allocated memory
|
||||
int Pointer; // Points to current location
|
||||
int FreeBlockOnClose; // As title
|
||||
|
||||
} FILEMEM;
|
||||
|
||||
static
|
||||
LPVOID MemoryOpen(LPBYTE Block, size_t Size, char Mode)
|
||||
{
|
||||
FILEMEM* fm = (FILEMEM*) _cmsMalloc(sizeof(FILEMEM));
|
||||
if (fm == NULL) return NULL;
|
||||
|
||||
ZeroMemory(fm, sizeof(FILEMEM));
|
||||
|
||||
if (Mode == 'r') {
|
||||
|
||||
fm ->Block = (LPBYTE) _cmsMalloc(Size);
|
||||
if (fm ->Block == NULL) {
|
||||
free(fm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CopyMemory(fm->Block, Block, Size);
|
||||
fm ->FreeBlockOnClose = TRUE;
|
||||
}
|
||||
else {
|
||||
fm ->Block = Block;
|
||||
fm ->FreeBlockOnClose = FALSE;
|
||||
}
|
||||
|
||||
fm ->Size = Size;
|
||||
fm ->Pointer = 0;
|
||||
|
||||
return (LPVOID) fm;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
size_t MemoryRead(LPVOID buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc)
|
||||
{
|
||||
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
|
||||
LPBYTE Ptr;
|
||||
size_t len = size * count;
|
||||
|
||||
|
||||
if (ResData -> Pointer + len > ResData -> Size){
|
||||
|
||||
len = (ResData -> Size - ResData -> Pointer);
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Got %d bytes, block should be of %d bytes", len * size, count * size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Ptr = ResData -> Block;
|
||||
Ptr += ResData -> Pointer;
|
||||
CopyMemory(buffer, Ptr, len);
|
||||
ResData -> Pointer += (int) len;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// SEEK_CUR is assumed
|
||||
|
||||
static
|
||||
LCMSBOOL MemorySeek(struct _lcms_iccprofile_struct* Icc, size_t offset)
|
||||
{
|
||||
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
|
||||
|
||||
if (offset > ResData ->Size) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Pointer error; probably corrupted file");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ResData ->Pointer = (DWORD) offset;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// FTell
|
||||
|
||||
static
|
||||
size_t MemoryTell(struct _lcms_iccprofile_struct* Icc)
|
||||
{
|
||||
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
|
||||
|
||||
return ResData -> Pointer;
|
||||
}
|
||||
|
||||
|
||||
// Writes data to memory, also keeps used space for further reference. NO CHECK IS PERFORMED
|
||||
|
||||
static
|
||||
LCMSBOOL MemoryWrite(struct _lcms_iccprofile_struct* Icc, size_t size, void *Ptr)
|
||||
{
|
||||
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
|
||||
|
||||
if (size == 0) return TRUE;
|
||||
|
||||
if (ResData != NULL)
|
||||
CopyMemory(ResData ->Block + Icc ->UsedSpace, Ptr, size);
|
||||
|
||||
Icc->UsedSpace += size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
LCMSBOOL MemoryClose(struct _lcms_iccprofile_struct* Icc)
|
||||
{
|
||||
FILEMEM* ResData = (FILEMEM*) Icc ->stream;
|
||||
|
||||
if (ResData ->FreeBlockOnClose) {
|
||||
|
||||
if (ResData ->Block) free(ResData ->Block);
|
||||
}
|
||||
free(ResData);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// File-based stream -------------------------------------------------------
|
||||
|
||||
static
|
||||
LPVOID FileOpen(const char* filename)
|
||||
{
|
||||
return (void*) fopen(filename, "rb");
|
||||
}
|
||||
|
||||
static
|
||||
size_t FileRead(void *buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc)
|
||||
{
|
||||
size_t nReaded = fread(buffer, size, count, (FILE*) Icc->stream);
|
||||
if (nReaded != count) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nReaded;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
LCMSBOOL FileSeek(struct _lcms_iccprofile_struct* Icc, size_t offset)
|
||||
{
|
||||
if (fseek((FILE*) Icc ->stream, (long) offset, SEEK_SET) != 0) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Seek error; probably corrupted file");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
size_t FileTell(struct _lcms_iccprofile_struct* Icc)
|
||||
{
|
||||
return ftell((FILE*) Icc ->stream);
|
||||
}
|
||||
|
||||
// Writes data to stream, also keeps used space for further reference
|
||||
|
||||
|
||||
static
|
||||
LCMSBOOL FileWrite(struct _lcms_iccprofile_struct* Icc, size_t size, LPVOID Ptr)
|
||||
{
|
||||
if (size == 0) return TRUE;
|
||||
|
||||
Icc->UsedSpace += size;
|
||||
|
||||
if (Icc->stream == NULL) {
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return (fwrite(Ptr, size, 1, (FILE*) Icc->stream) == 1);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
LCMSBOOL FileClose(struct _lcms_iccprofile_struct* Icc)
|
||||
{
|
||||
return fclose((FILE*) Icc ->stream);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Creates an empty structure holding all required parameters
|
||||
|
||||
cmsHPROFILE _cmsCreateProfilePlaceholder(void)
|
||||
{
|
||||
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) _cmsMalloc(sizeof(LCMSICCPROFILE));
|
||||
if (Icc == NULL) return NULL;
|
||||
|
||||
// Empty values
|
||||
ZeroMemory(Icc, sizeof(LCMSICCPROFILE));
|
||||
|
||||
// Make sure illuminant is correct
|
||||
Icc ->Illuminant = *cmsD50_XYZ();
|
||||
|
||||
// Set it to empty
|
||||
Icc -> TagCount = 0;
|
||||
|
||||
// Return the handle
|
||||
return (cmsHPROFILE) Icc;
|
||||
}
|
||||
|
||||
|
||||
// Return the number of tags
|
||||
icInt32Number LCMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return Icc->TagCount;
|
||||
}
|
||||
|
||||
// Return the tag signature of a given tag number
|
||||
icTagSignature LCMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, icInt32Number n)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
|
||||
if (n < 0 || n > Icc->TagCount) return (icTagSignature) 0; // Mark as not available
|
||||
|
||||
return Icc ->TagNames[n];
|
||||
}
|
||||
|
||||
|
||||
// Search for a specific tag in tag dictionary
|
||||
// Returns position or -1 if tag not found
|
||||
|
||||
icInt32Number _cmsSearchTag(LPLCMSICCPROFILE Profile, icTagSignature sig, LCMSBOOL lSignalError)
|
||||
{
|
||||
icInt32Number i;
|
||||
|
||||
if (sig == 0) return -1; // 0 identifies a special tag holding raw memory.
|
||||
|
||||
for (i=0; i < Profile -> TagCount; i++) {
|
||||
|
||||
if (sig == Profile -> TagNames[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
if (lSignalError)
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Tag '%lx' not found", sig);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Check existance
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
return _cmsSearchTag(Icc, sig, FALSE) >= 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Search for a particular tag, replace if found or add new one else
|
||||
|
||||
LPVOID _cmsInitTag(LPLCMSICCPROFILE Icc, icTagSignature sig, size_t size, const void* Init)
|
||||
{
|
||||
LPVOID Ptr;
|
||||
icInt32Number i;
|
||||
|
||||
i = _cmsSearchTag(Icc, sig, FALSE);
|
||||
|
||||
if (i >=0) {
|
||||
|
||||
if (Icc -> TagPtrs[i]) free(Icc -> TagPtrs[i]);
|
||||
}
|
||||
else {
|
||||
|
||||
i = Icc -> TagCount;
|
||||
Icc -> TagCount++;
|
||||
|
||||
if (Icc ->TagCount >= MAX_TABLE_TAG) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", MAX_TABLE_TAG);
|
||||
Icc ->TagCount = MAX_TABLE_TAG-1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ptr = _cmsMalloc(size);
|
||||
if (Ptr == NULL) return NULL;
|
||||
|
||||
CopyMemory(Ptr, Init, size);
|
||||
|
||||
Icc ->TagNames[i] = sig;
|
||||
Icc ->TagSizes[i] = size;
|
||||
Icc ->TagPtrs[i] = Ptr;
|
||||
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Creates a profile from file read placeholder
|
||||
|
||||
LPLCMSICCPROFILE _cmsCreateProfileFromFilePlaceholder(const char* FileName)
|
||||
{
|
||||
LPLCMSICCPROFILE NewIcc;
|
||||
LPVOID ICCfile = FileOpen(FileName);
|
||||
|
||||
if (ICCfile == NULL) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder();
|
||||
if (NewIcc == NULL) return NULL;
|
||||
|
||||
strncpy(NewIcc -> PhysicalFile, FileName, MAX_PATH-1);
|
||||
NewIcc -> PhysicalFile[MAX_PATH-1] = 0;
|
||||
|
||||
NewIcc ->stream = ICCfile;
|
||||
|
||||
NewIcc ->Read = FileRead;
|
||||
NewIcc ->Seek = FileSeek;
|
||||
NewIcc ->Tell = FileTell;
|
||||
NewIcc ->Close = FileClose;
|
||||
NewIcc ->Write = NULL;
|
||||
|
||||
NewIcc ->IsWrite = FALSE;
|
||||
|
||||
|
||||
|
||||
|
||||
return NewIcc;
|
||||
}
|
||||
|
||||
|
||||
// Creates a profile from memory read placeholder
|
||||
|
||||
LPLCMSICCPROFILE _cmsCreateProfileFromMemPlaceholder(LPVOID MemPtr, DWORD dwSize)
|
||||
{
|
||||
|
||||
LPLCMSICCPROFILE NewIcc;
|
||||
LPVOID ICCfile = MemoryOpen((LPBYTE) MemPtr, (size_t) dwSize, 'r');
|
||||
|
||||
|
||||
if (ICCfile == NULL) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't allocate %ld bytes for profile", dwSize);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder();
|
||||
if (NewIcc == NULL) return NULL;
|
||||
|
||||
NewIcc -> PhysicalFile[0] = 0;
|
||||
NewIcc ->stream = ICCfile;
|
||||
|
||||
NewIcc ->Read = MemoryRead;
|
||||
NewIcc ->Seek = MemorySeek;
|
||||
NewIcc ->Tell = MemoryTell;
|
||||
NewIcc ->Close = MemoryClose;
|
||||
NewIcc ->Write = NULL;
|
||||
|
||||
NewIcc ->IsWrite = FALSE;
|
||||
|
||||
|
||||
return NewIcc;
|
||||
}
|
||||
|
||||
|
||||
// Turn a placeholder into file writter
|
||||
|
||||
void _cmsSetSaveToDisk(LPLCMSICCPROFILE Icc, const char* FileName)
|
||||
{
|
||||
|
||||
if (FileName == NULL) {
|
||||
|
||||
Icc ->stream = NULL;
|
||||
}
|
||||
else {
|
||||
|
||||
Icc ->stream = fopen(FileName, "wb");
|
||||
if (Icc ->stream == NULL)
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to file '%s'", FileName);
|
||||
}
|
||||
|
||||
Icc ->Write = FileWrite; // Save to disk
|
||||
Icc ->Close = FileClose;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Turn a placeholder into memory writter
|
||||
|
||||
void _cmsSetSaveToMemory(LPLCMSICCPROFILE Icc, LPVOID MemPtr, size_t dwSize)
|
||||
{
|
||||
|
||||
if (MemPtr == NULL) {
|
||||
|
||||
Icc ->stream = NULL;
|
||||
}
|
||||
else {
|
||||
|
||||
Icc ->stream = (FILEMEM*) MemoryOpen((LPBYTE) MemPtr, dwSize, 'w');
|
||||
if (Icc ->stream == NULL)
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to memory");
|
||||
}
|
||||
|
||||
Icc ->Write = MemoryWrite;
|
||||
Icc ->Close = MemoryClose;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- Set/Get several struct members
|
||||
|
||||
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
*Dest = Icc -> MediaWhitePoint;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
*Dest = Icc -> MediaBlackPoint;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
*Dest = Icc -> Illuminant;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int LCMSEXPORT cmsTakeRenderingIntent(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return (int) Icc -> RenderingIntent;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> RenderingIntent = (icRenderingIntent) RenderingIntent;
|
||||
}
|
||||
|
||||
|
||||
DWORD LCMSEXPORT cmsTakeHeaderFlags(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return (DWORD) Icc -> flags;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, DWORD Flags)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> flags = (icUInt32Number) Flags;
|
||||
}
|
||||
|
||||
DWORD LCMSEXPORT cmsTakeHeaderAttributes(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return (DWORD) Icc -> attributes;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, DWORD Flags)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> attributes = (icUInt32Number) Flags;
|
||||
}
|
||||
|
||||
|
||||
const BYTE* LCMSEXPORT cmsTakeProfileID(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return Icc ->ProfileID;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetProfileID(cmsHPROFILE hProfile, LPBYTE ProfileID)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
CopyMemory(Icc -> ProfileID, ProfileID, 16);
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsTakeCreationDateTime(struct tm *Dest, cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
CopyMemory(Dest, &Icc ->Created, sizeof(struct tm));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
icColorSpaceSignature LCMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return Icc -> PCS;
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> PCS = pcs;
|
||||
}
|
||||
|
||||
icColorSpaceSignature LCMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return Icc -> ColorSpace;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> ColorSpace = sig;
|
||||
}
|
||||
|
||||
icProfileClassSignature LCMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return Icc -> DeviceClass;
|
||||
}
|
||||
|
||||
DWORD LCMSEXPORT cmsGetProfileICCversion(cmsHPROFILE hProfile)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
return (DWORD) Icc -> Version;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsSetProfileICCversion(cmsHPROFILE hProfile, DWORD Version)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> Version = Version;
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
|
||||
Icc -> DeviceClass = sig;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
static
|
||||
int SizeOfGammaTab(LPGAMMATABLE In)
|
||||
{
|
||||
return sizeof(GAMMATABLE) + (In -> nEntries - 1)*sizeof(WORD);
|
||||
}
|
||||
|
||||
|
||||
// Creates a phantom tag holding a memory block
|
||||
|
||||
static
|
||||
LPVOID DupBlock(LPLCMSICCPROFILE Icc, LPVOID Block, size_t size)
|
||||
{
|
||||
if (Block != NULL && size > 0)
|
||||
return _cmsInitTag(Icc, (icTagSignature) 0, size, Block);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
// This is tricky, since LUT structs does have pointers
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddLUTTag(cmsHPROFILE hProfile, icTagSignature sig, const void* lut)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
LPLUT Orig, Stored;
|
||||
unsigned int i;
|
||||
|
||||
// The struct itself
|
||||
|
||||
Orig = (LPLUT) lut;
|
||||
Stored = (LPLUT) _cmsInitTag(Icc, (icTagSignature) sig, sizeof(LUT), lut);
|
||||
|
||||
// dup' the memory blocks
|
||||
for (i=0; i < Orig ->InputChan; i++)
|
||||
Stored -> L1[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L1[i],
|
||||
sizeof(WORD) * Orig ->In16params.nSamples);
|
||||
|
||||
for (i=0; i < Orig ->OutputChan; i++)
|
||||
Stored -> L2[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L2[i],
|
||||
sizeof(WORD) * Orig ->Out16params.nSamples);
|
||||
|
||||
Stored -> T = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->T, Orig -> Tsize);
|
||||
|
||||
// Zero any additional pointer
|
||||
Stored ->CLut16params.p8 = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddXYZTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* XYZ)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, sizeof(cmsCIEXYZ), XYZ);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddTextTag(cmsHPROFILE hProfile, icTagSignature sig, const char* Text)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, strlen(Text)+1, (LPVOID) Text);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddGammaTag(cmsHPROFILE hProfile, icTagSignature sig, LPGAMMATABLE TransferFunction)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, SizeOfGammaTab(TransferFunction), TransferFunction);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddChromaticityTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIExyYTRIPLE Chrm)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, sizeof(cmsCIExyYTRIPLE), Chrm);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddSequenceDescriptionTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsSEQ pseq)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, sizeof(int) + pseq -> n * sizeof(cmsPSEQDESC), pseq);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddNamedColorTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddDateTimeTag(cmsHPROFILE hProfile, icTagSignature sig, struct tm *DateTime)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, sizeof(struct tm), DateTime);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddColorantTableTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT _cmsAddChromaticAdaptationTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* mat)
|
||||
{
|
||||
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
|
||||
|
||||
_cmsInitTag(Icc, sig, 3*sizeof(cmsCIEXYZ), mat);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,809 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
// Pipeline of LUT. Enclosed by {} are new revision 4.0 of ICC spec.
|
||||
//
|
||||
// [Mat] -> [L1] -> { [Mat3] -> [Ofs3] -> [L3] ->} [CLUT] { -> [L4] -> [Mat4] -> [Ofs4] } -> [L2]
|
||||
//
|
||||
// Some of these stages would be missing. This implements the totality of
|
||||
// combinations of old and new LUT types as follows:
|
||||
//
|
||||
// Lut8 & Lut16
|
||||
// ============
|
||||
// [Mat] -> [L1] -> [CLUT] -> [L2]
|
||||
//
|
||||
// Mat2, Ofs2, L3, L3, Mat3, Ofs3 are missing
|
||||
//
|
||||
// LutAToB
|
||||
// ========
|
||||
//
|
||||
// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
|
||||
//
|
||||
// Mat, Mat3, Ofs3, L3 are missing
|
||||
// L1 = A curves
|
||||
// L4 = M curves
|
||||
// L2 = B curves
|
||||
//
|
||||
// LutBToA
|
||||
// =======
|
||||
//
|
||||
// [L1] -> [Mat3] -> [Ofs3] -> [L3] -> [CLUT] -> [L2]
|
||||
//
|
||||
// Mat, L4, Mat4, Ofs4 are missing
|
||||
// L1 = B Curves
|
||||
// L3 = M Curves
|
||||
// L2 = A curves
|
||||
//
|
||||
//
|
||||
// V2&3 emulation
|
||||
// ===============
|
||||
//
|
||||
// For output, Mat is multiplied by
|
||||
//
|
||||
//
|
||||
// | 0xff00 / 0xffff 0 0 |
|
||||
// | 0 0xff00 / 0xffff 0 |
|
||||
// | 0 0 0xff00 / 0xffff |
|
||||
//
|
||||
//
|
||||
// For input, an additional matrix is needed at the very last end of the chain
|
||||
//
|
||||
//
|
||||
// | 0xffff / 0xff00 0 0 |
|
||||
// | 0 0xffff / 0xff00 0 |
|
||||
// | 0 0 0xffff / 0xff00 |
|
||||
//
|
||||
//
|
||||
// Which reduces to (val * 257) >> 8
|
||||
|
||||
// A couple of macros to convert between revisions
|
||||
|
||||
#define FROM_V2_TO_V4(x) (((((x)<<8)+(x))+0x80)>>8) // BY 65535 DIV 65280 ROUND
|
||||
#define FROM_V4_TO_V2(x) ((((x)<<8)+0x80)/257) // BY 65280 DIV 65535 ROUND
|
||||
|
||||
|
||||
// Lut Creation & Destruction
|
||||
|
||||
LPLUT LCMSEXPORT cmsAllocLUT(void)
|
||||
{
|
||||
LPLUT NewLUT;
|
||||
|
||||
NewLUT = (LPLUT) _cmsMalloc(sizeof(LUT));
|
||||
if (NewLUT)
|
||||
ZeroMemory(NewLUT, sizeof(LUT));
|
||||
|
||||
return NewLUT;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsFreeLUT(LPLUT Lut)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!Lut) return;
|
||||
|
||||
if (Lut -> T) free(Lut -> T);
|
||||
|
||||
for (i=0; i < Lut -> OutputChan; i++)
|
||||
{
|
||||
if (Lut -> L2[i]) free(Lut -> L2[i]);
|
||||
}
|
||||
|
||||
for (i=0; i < Lut -> InputChan; i++)
|
||||
{
|
||||
|
||||
if (Lut -> L1[i]) free(Lut -> L1[i]);
|
||||
}
|
||||
|
||||
|
||||
if (Lut ->wFlags & LUT_HASTL3) {
|
||||
|
||||
for (i=0; i < Lut -> InputChan; i++) {
|
||||
|
||||
if (Lut -> L3[i]) free(Lut -> L3[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (Lut ->wFlags & LUT_HASTL4) {
|
||||
|
||||
for (i=0; i < Lut -> OutputChan; i++) {
|
||||
|
||||
if (Lut -> L4[i]) free(Lut -> L4[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (Lut ->CLut16params.p8)
|
||||
free(Lut ->CLut16params.p8);
|
||||
|
||||
free(Lut);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
LPVOID DupBlockTab(LPVOID Org, size_t size)
|
||||
{
|
||||
LPVOID mem = _cmsMalloc(size);
|
||||
if (mem != NULL)
|
||||
CopyMemory(mem, Org, size);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
LPLUT LCMSEXPORT cmsDupLUT(LPLUT Orig)
|
||||
{
|
||||
LPLUT NewLUT = cmsAllocLUT();
|
||||
unsigned int i;
|
||||
|
||||
CopyMemory(NewLUT, Orig, sizeof(LUT));
|
||||
|
||||
for (i=0; i < Orig ->InputChan; i++)
|
||||
NewLUT -> L1[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L1[i],
|
||||
sizeof(WORD) * Orig ->In16params.nSamples);
|
||||
|
||||
for (i=0; i < Orig ->OutputChan; i++)
|
||||
NewLUT -> L2[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L2[i],
|
||||
sizeof(WORD) * Orig ->Out16params.nSamples);
|
||||
|
||||
NewLUT -> T = (LPWORD) DupBlockTab((LPVOID) Orig ->T, Orig -> Tsize);
|
||||
|
||||
return NewLUT;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
unsigned int UIpow(unsigned int a, unsigned int b)
|
||||
{
|
||||
unsigned int rv = 1;
|
||||
|
||||
for (; b > 0; b--)
|
||||
rv *= a;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
LPLUT LCMSEXPORT cmsAlloc3DGrid(LPLUT NewLUT, int clutPoints, int inputChan, int outputChan)
|
||||
{
|
||||
DWORD nTabSize;
|
||||
|
||||
NewLUT -> wFlags |= LUT_HAS3DGRID;
|
||||
NewLUT -> cLutPoints = clutPoints;
|
||||
NewLUT -> InputChan = inputChan;
|
||||
NewLUT -> OutputChan = outputChan;
|
||||
|
||||
|
||||
nTabSize = (NewLUT -> OutputChan * UIpow(NewLUT->cLutPoints,
|
||||
NewLUT->InputChan)
|
||||
* sizeof(WORD));
|
||||
|
||||
NewLUT -> T = (LPWORD) _cmsMalloc(nTabSize);
|
||||
if (NewLUT -> T == NULL) return NULL;
|
||||
|
||||
ZeroMemory(NewLUT -> T, nTabSize);
|
||||
NewLUT ->Tsize = nTabSize;
|
||||
|
||||
|
||||
cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
|
||||
NewLUT -> OutputChan,
|
||||
&NewLUT -> CLut16params);
|
||||
|
||||
return NewLUT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LPLUT LCMSEXPORT cmsAllocLinearTable(LPLUT NewLUT, LPGAMMATABLE Tables[], int nTable)
|
||||
{
|
||||
unsigned int i;
|
||||
LPWORD PtrW;
|
||||
|
||||
switch (nTable) {
|
||||
|
||||
|
||||
case 1: NewLUT -> wFlags |= LUT_HASTL1;
|
||||
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> In16params);
|
||||
NewLUT -> InputEntries = Tables[0] -> nEntries;
|
||||
|
||||
for (i=0; i < NewLUT -> InputChan; i++) {
|
||||
|
||||
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries);
|
||||
if (PtrW == NULL) return NULL;
|
||||
|
||||
NewLUT -> L1[i] = PtrW;
|
||||
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> InputEntries);
|
||||
CopyMemory(&NewLUT -> LCurvesSeed[0][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case 2: NewLUT -> wFlags |= LUT_HASTL2;
|
||||
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> Out16params);
|
||||
NewLUT -> OutputEntries = Tables[0] -> nEntries;
|
||||
for (i=0; i < NewLUT -> OutputChan; i++) {
|
||||
|
||||
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries);
|
||||
if (PtrW == NULL) return NULL;
|
||||
|
||||
NewLUT -> L2[i] = PtrW;
|
||||
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> OutputEntries);
|
||||
CopyMemory(&NewLUT -> LCurvesSeed[1][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// 3 & 4 according ICC 4.0 spec
|
||||
|
||||
case 3:
|
||||
NewLUT -> wFlags |= LUT_HASTL3;
|
||||
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L3params);
|
||||
NewLUT -> L3Entries = Tables[0] -> nEntries;
|
||||
|
||||
for (i=0; i < NewLUT -> InputChan; i++) {
|
||||
|
||||
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L3Entries);
|
||||
if (PtrW == NULL) return NULL;
|
||||
|
||||
NewLUT -> L3[i] = PtrW;
|
||||
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L3Entries);
|
||||
CopyMemory(&NewLUT -> LCurvesSeed[2][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
NewLUT -> wFlags |= LUT_HASTL4;
|
||||
cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L4params);
|
||||
NewLUT -> L4Entries = Tables[0] -> nEntries;
|
||||
for (i=0; i < NewLUT -> OutputChan; i++) {
|
||||
|
||||
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L4Entries);
|
||||
if (PtrW == NULL) return NULL;
|
||||
|
||||
NewLUT -> L4[i] = PtrW;
|
||||
CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L4Entries);
|
||||
CopyMemory(&NewLUT -> LCurvesSeed[3][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
return NewLUT;
|
||||
}
|
||||
|
||||
|
||||
// Set the LUT matrix
|
||||
|
||||
LPLUT LCMSEXPORT cmsSetMatrixLUT(LPLUT Lut, LPMAT3 M)
|
||||
{
|
||||
MAT3toFix(&Lut ->Matrix, M);
|
||||
|
||||
if (!MAT3isIdentity(&Lut->Matrix, 0.0001))
|
||||
Lut ->wFlags |= LUT_HASMATRIX;
|
||||
|
||||
return Lut;
|
||||
}
|
||||
|
||||
|
||||
// Set matrix & offset, v4 compatible
|
||||
|
||||
LPLUT LCMSEXPORT cmsSetMatrixLUT4(LPLUT Lut, LPMAT3 M, LPVEC3 off, DWORD dwFlags)
|
||||
{
|
||||
WMAT3 WMat;
|
||||
WVEC3 Woff;
|
||||
VEC3 Zero = {{0, 0, 0}};
|
||||
|
||||
MAT3toFix(&WMat, M);
|
||||
|
||||
if (off == NULL)
|
||||
off = &Zero;
|
||||
|
||||
VEC3toFix(&Woff, off);
|
||||
|
||||
// Nop if identity
|
||||
if (MAT3isIdentity(&WMat, 0.0001) &&
|
||||
(Woff.n[VX] == 0 && Woff.n[VY] == 0 && Woff.n[VZ] == 0))
|
||||
return Lut;
|
||||
|
||||
switch (dwFlags) {
|
||||
|
||||
case LUT_HASMATRIX:
|
||||
Lut ->Matrix = WMat;
|
||||
Lut ->wFlags |= LUT_HASMATRIX;
|
||||
break;
|
||||
|
||||
case LUT_HASMATRIX3:
|
||||
Lut ->Mat3 = WMat;
|
||||
Lut ->Ofs3 = Woff;
|
||||
Lut ->wFlags |= LUT_HASMATRIX3;
|
||||
break;
|
||||
|
||||
case LUT_HASMATRIX4:
|
||||
Lut ->Mat4 = WMat;
|
||||
Lut ->Ofs4 = Woff;
|
||||
Lut ->wFlags |= LUT_HASMATRIX4;
|
||||
break;
|
||||
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
return Lut;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// The full evaluator
|
||||
|
||||
void LCMSEXPORT cmsEvalLUT(LPLUT Lut, WORD In[], WORD Out[])
|
||||
{
|
||||
register unsigned int i;
|
||||
WORD StageABC[MAXCHANNELS], StageLMN[MAXCHANNELS];
|
||||
|
||||
|
||||
// Try to speedup things on plain devicelinks
|
||||
if (Lut ->wFlags == LUT_HAS3DGRID) {
|
||||
|
||||
Lut ->CLut16params.Interp3D(In, Out, Lut -> T, &Lut -> CLut16params);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Nope, evaluate whole LUT
|
||||
|
||||
for (i=0; i < Lut -> InputChan; i++)
|
||||
StageABC[i] = In[i];
|
||||
|
||||
|
||||
if (Lut ->wFlags & LUT_V4_OUTPUT_EMULATE_V2) {
|
||||
|
||||
// Clamp Lab to avoid overflow
|
||||
if (StageABC[0] > 0xFF00)
|
||||
StageABC[0] = 0xFF00;
|
||||
|
||||
StageABC[0] = (WORD) FROM_V2_TO_V4(StageABC[0]);
|
||||
StageABC[1] = (WORD) FROM_V2_TO_V4(StageABC[1]);
|
||||
StageABC[2] = (WORD) FROM_V2_TO_V4(StageABC[2]);
|
||||
|
||||
}
|
||||
|
||||
if (Lut ->wFlags & LUT_V2_OUTPUT_EMULATE_V4) {
|
||||
|
||||
StageABC[0] = (WORD) FROM_V4_TO_V2(StageABC[0]);
|
||||
StageABC[1] = (WORD) FROM_V4_TO_V2(StageABC[1]);
|
||||
StageABC[2] = (WORD) FROM_V4_TO_V2(StageABC[2]);
|
||||
}
|
||||
|
||||
|
||||
// Matrix handling.
|
||||
|
||||
if (Lut -> wFlags & LUT_HASMATRIX) {
|
||||
|
||||
WVEC3 InVect, OutVect;
|
||||
|
||||
// In LUT8 here comes the special gray axis fixup
|
||||
|
||||
if (Lut ->FixGrayAxes) {
|
||||
|
||||
StageABC[1] = _cmsClampWord(StageABC[1] - 128);
|
||||
StageABC[2] = _cmsClampWord(StageABC[2] - 128);
|
||||
}
|
||||
|
||||
// Matrix
|
||||
|
||||
InVect.n[VX] = ToFixedDomain(StageABC[0]);
|
||||
InVect.n[VY] = ToFixedDomain(StageABC[1]);
|
||||
InVect.n[VZ] = ToFixedDomain(StageABC[2]);
|
||||
|
||||
|
||||
MAT3evalW(&OutVect, &Lut -> Matrix, &InVect);
|
||||
|
||||
// PCS in 1Fixed15 format, adjusting
|
||||
|
||||
StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
|
||||
StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
|
||||
StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
|
||||
}
|
||||
|
||||
|
||||
// First linearization
|
||||
|
||||
if (Lut -> wFlags & LUT_HASTL1)
|
||||
{
|
||||
for (i=0; i < Lut -> InputChan; i++)
|
||||
StageABC[i] = cmsLinearInterpLUT16(StageABC[i],
|
||||
Lut -> L1[i],
|
||||
&Lut -> In16params);
|
||||
}
|
||||
|
||||
|
||||
// Mat3, Ofs3, L3 processing
|
||||
|
||||
if (Lut ->wFlags & LUT_HASMATRIX3) {
|
||||
|
||||
WVEC3 InVect, OutVect;
|
||||
|
||||
InVect.n[VX] = ToFixedDomain(StageABC[0]);
|
||||
InVect.n[VY] = ToFixedDomain(StageABC[1]);
|
||||
InVect.n[VZ] = ToFixedDomain(StageABC[2]);
|
||||
|
||||
MAT3evalW(&OutVect, &Lut -> Mat3, &InVect);
|
||||
|
||||
OutVect.n[VX] += Lut ->Ofs3.n[VX];
|
||||
OutVect.n[VY] += Lut ->Ofs3.n[VY];
|
||||
OutVect.n[VZ] += Lut ->Ofs3.n[VZ];
|
||||
|
||||
StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
|
||||
StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
|
||||
StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
|
||||
|
||||
}
|
||||
|
||||
if (Lut ->wFlags & LUT_HASTL3) {
|
||||
|
||||
for (i=0; i < Lut -> InputChan; i++)
|
||||
StageABC[i] = cmsLinearInterpLUT16(StageABC[i],
|
||||
Lut -> L3[i],
|
||||
&Lut -> L3params);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (Lut -> wFlags & LUT_HAS3DGRID) {
|
||||
|
||||
Lut ->CLut16params.Interp3D(StageABC, StageLMN, Lut -> T, &Lut -> CLut16params);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (i=0; i < Lut -> InputChan; i++)
|
||||
StageLMN[i] = StageABC[i];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Mat4, Ofs4, L4 processing
|
||||
|
||||
if (Lut ->wFlags & LUT_HASTL4) {
|
||||
|
||||
for (i=0; i < Lut -> OutputChan; i++)
|
||||
StageLMN[i] = cmsLinearInterpLUT16(StageLMN[i],
|
||||
Lut -> L4[i],
|
||||
&Lut -> L4params);
|
||||
}
|
||||
|
||||
if (Lut ->wFlags & LUT_HASMATRIX4) {
|
||||
|
||||
WVEC3 InVect, OutVect;
|
||||
|
||||
InVect.n[VX] = ToFixedDomain(StageLMN[0]);
|
||||
InVect.n[VY] = ToFixedDomain(StageLMN[1]);
|
||||
InVect.n[VZ] = ToFixedDomain(StageLMN[2]);
|
||||
|
||||
MAT3evalW(&OutVect, &Lut -> Mat4, &InVect);
|
||||
|
||||
OutVect.n[VX] += Lut ->Ofs4.n[VX];
|
||||
OutVect.n[VY] += Lut ->Ofs4.n[VY];
|
||||
OutVect.n[VZ] += Lut ->Ofs4.n[VZ];
|
||||
|
||||
StageLMN[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
|
||||
StageLMN[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
|
||||
StageLMN[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
|
||||
|
||||
}
|
||||
|
||||
// Last linearitzation
|
||||
|
||||
if (Lut -> wFlags & LUT_HASTL2)
|
||||
{
|
||||
for (i=0; i < Lut -> OutputChan; i++)
|
||||
Out[i] = cmsLinearInterpLUT16(StageLMN[i],
|
||||
Lut -> L2[i],
|
||||
&Lut -> Out16params);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i < Lut -> OutputChan; i++)
|
||||
Out[i] = StageLMN[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (Lut ->wFlags & LUT_V4_INPUT_EMULATE_V2) {
|
||||
|
||||
Out[0] = (WORD) FROM_V4_TO_V2(Out[0]);
|
||||
Out[1] = (WORD) FROM_V4_TO_V2(Out[1]);
|
||||
Out[2] = (WORD) FROM_V4_TO_V2(Out[2]);
|
||||
|
||||
}
|
||||
|
||||
if (Lut ->wFlags & LUT_V2_INPUT_EMULATE_V4) {
|
||||
|
||||
Out[0] = (WORD) FROM_V2_TO_V4(Out[0]);
|
||||
Out[1] = (WORD) FROM_V2_TO_V4(Out[1]);
|
||||
Out[2] = (WORD) FROM_V2_TO_V4(Out[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Precomputes tables for 8-bit on input devicelink.
|
||||
//
|
||||
LPLUT _cmsBlessLUT8(LPLUT Lut)
|
||||
{
|
||||
int i, j;
|
||||
WORD StageABC[3];
|
||||
Fixed32 v1, v2, v3;
|
||||
LPL8PARAMS p8;
|
||||
LPL16PARAMS p = &Lut ->CLut16params;
|
||||
|
||||
|
||||
p8 = (LPL8PARAMS) _cmsMalloc(sizeof(L8PARAMS));
|
||||
if (p8 == NULL) return NULL;
|
||||
|
||||
// values comes * 257, so we can safely take first byte (x << 8 + x)
|
||||
// if there are prelinearization, is already smelted in tables
|
||||
|
||||
for (i=0; i < 256; i++) {
|
||||
|
||||
StageABC[0] = StageABC[1] = StageABC[2] = RGB_8_TO_16(i);
|
||||
|
||||
if (Lut ->wFlags & LUT_HASTL1) {
|
||||
|
||||
for (j=0; j < 3; j++)
|
||||
StageABC[j] = cmsLinearInterpLUT16(StageABC[j],
|
||||
Lut -> L1[j],
|
||||
&Lut -> In16params);
|
||||
Lut ->wFlags &= ~LUT_HASTL1;
|
||||
}
|
||||
|
||||
|
||||
v1 = ToFixedDomain(StageABC[0] * p -> Domain);
|
||||
v2 = ToFixedDomain(StageABC[1] * p -> Domain);
|
||||
v3 = ToFixedDomain(StageABC[2] * p -> Domain);
|
||||
|
||||
p8 ->X0[i] = p->opta3 * FIXED_TO_INT(v1);
|
||||
p8 ->Y0[i] = p->opta2 * FIXED_TO_INT(v2);
|
||||
p8 ->Z0[i] = p->opta1 * FIXED_TO_INT(v3);
|
||||
|
||||
p8 ->rx[i] = (WORD) FIXED_REST_TO_INT(v1);
|
||||
p8 ->ry[i] = (WORD) FIXED_REST_TO_INT(v2);
|
||||
p8 ->rz[i] = (WORD) FIXED_REST_TO_INT(v3);
|
||||
|
||||
}
|
||||
|
||||
Lut -> CLut16params.p8 = p8;
|
||||
Lut -> CLut16params.Interp3D = cmsTetrahedralInterp8;
|
||||
|
||||
return Lut;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------- Reverse interpolation
|
||||
|
||||
|
||||
// Here's how it goes. The derivative Df(x) of the function f is the linear
|
||||
// transformation that best approximates f near the point x. It can be represented
|
||||
// by a matrix A whose entries are the partial derivatives of the components of f
|
||||
// with respect to all the coordinates. This is know as the Jacobian
|
||||
//
|
||||
// The best linear approximation to f is given by the matrix equation:
|
||||
//
|
||||
// y-y0 = A (x-x0)
|
||||
//
|
||||
// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this
|
||||
// linear approximation will give a "better guess" for the zero of f. Thus let y=0,
|
||||
// and since y0=f(x0) one can solve the above equation for x. This leads to the
|
||||
// Newton's method formula:
|
||||
//
|
||||
// xn+1 = xn - A-1 f(xn)
|
||||
//
|
||||
// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the
|
||||
// fashion described above. Iterating this will give better and better approximations
|
||||
// if you have a "good enough" initial guess.
|
||||
|
||||
|
||||
#define JACOBIAN_EPSILON 0.001
|
||||
#define INVERSION_MAX_ITERATIONS 30
|
||||
|
||||
|
||||
|
||||
// Increment with reflexion on boundary
|
||||
|
||||
static
|
||||
void IncDelta(double *Val)
|
||||
{
|
||||
if (*Val < (1.0 - JACOBIAN_EPSILON))
|
||||
|
||||
*Val += JACOBIAN_EPSILON;
|
||||
|
||||
else
|
||||
*Val -= JACOBIAN_EPSILON;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
void ToEncoded(WORD Encoded[3], LPVEC3 Float)
|
||||
{
|
||||
Encoded[0] = (WORD) floor(Float->n[0] * 65535.0 + 0.5);
|
||||
Encoded[1] = (WORD) floor(Float->n[1] * 65535.0 + 0.5);
|
||||
Encoded[2] = (WORD) floor(Float->n[2] * 65535.0 + 0.5);
|
||||
}
|
||||
|
||||
static
|
||||
void FromEncoded(LPVEC3 Float, WORD Encoded[3])
|
||||
{
|
||||
Float->n[0] = Encoded[0] / 65535.0;
|
||||
Float->n[1] = Encoded[1] / 65535.0;
|
||||
Float->n[2] = Encoded[2] / 65535.0;
|
||||
}
|
||||
|
||||
// Evaluates the CLUT part of a LUT (4 -> 3 only)
|
||||
static
|
||||
void EvalLUTdoubleKLab(LPLUT Lut, const VEC3* In, WORD FixedK, LPcmsCIELab Out)
|
||||
{
|
||||
WORD wIn[4], wOut[3];
|
||||
|
||||
wIn[0] = (WORD) floor(In ->n[0] * 65535.0 + 0.5);
|
||||
wIn[1] = (WORD) floor(In ->n[1] * 65535.0 + 0.5);
|
||||
wIn[2] = (WORD) floor(In ->n[2] * 65535.0 + 0.5);
|
||||
wIn[3] = FixedK;
|
||||
|
||||
cmsEvalLUT(Lut, wIn, wOut);
|
||||
cmsLabEncoded2Float(Out, wOut);
|
||||
}
|
||||
|
||||
// Builds a Jacobian CMY->Lab
|
||||
|
||||
static
|
||||
void ComputeJacobianLab(LPLUT Lut, LPMAT3 Jacobian, const VEC3* Colorant, WORD K)
|
||||
{
|
||||
VEC3 ColorantD;
|
||||
cmsCIELab Lab, LabD;
|
||||
int j;
|
||||
|
||||
EvalLUTdoubleKLab(Lut, Colorant, K, &Lab);
|
||||
|
||||
|
||||
for (j = 0; j < 3; j++) {
|
||||
|
||||
ColorantD.n[0] = Colorant ->n[0];
|
||||
ColorantD.n[1] = Colorant ->n[1];
|
||||
ColorantD.n[2] = Colorant ->n[2];
|
||||
|
||||
IncDelta(&ColorantD.n[j]);
|
||||
|
||||
EvalLUTdoubleKLab(Lut, &ColorantD, K, &LabD);
|
||||
|
||||
Jacobian->v[0].n[j] = ((LabD.L - Lab.L) / JACOBIAN_EPSILON);
|
||||
Jacobian->v[1].n[j] = ((LabD.a - Lab.a) / JACOBIAN_EPSILON);
|
||||
Jacobian->v[2].n[j] = ((LabD.b - Lab.b) / JACOBIAN_EPSILON);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT, but It
|
||||
// can be used on CMYK -> Lab LUT to obtain black preservation.
|
||||
// Target holds LabK in this case
|
||||
|
||||
// x1 <- x - [J(x)]^-1 * f(x)
|
||||
|
||||
|
||||
LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint)
|
||||
{
|
||||
int i;
|
||||
double error, LastError = 1E20;
|
||||
cmsCIELab fx, Goal;
|
||||
VEC3 tmp, tmp2, x;
|
||||
MAT3 Jacobian;
|
||||
WORD FixedK;
|
||||
WORD LastResult[4];
|
||||
|
||||
|
||||
// This is our Lab goal
|
||||
cmsLabEncoded2Float(&Goal, Target);
|
||||
|
||||
// Special case for CMYK->Lab
|
||||
|
||||
if (Lut ->InputChan == 4)
|
||||
FixedK = Target[3];
|
||||
else
|
||||
FixedK = 0;
|
||||
|
||||
|
||||
// Take the hint as starting point if specified
|
||||
|
||||
if (Hint == NULL) {
|
||||
|
||||
// Begin at any point, we choose 1/3 of neutral CMY gray
|
||||
|
||||
x.n[0] = x.n[1] = x.n[2] = 0.3;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
FromEncoded(&x, Hint);
|
||||
}
|
||||
|
||||
|
||||
// Iterate
|
||||
|
||||
for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {
|
||||
|
||||
// Get beginning fx
|
||||
EvalLUTdoubleKLab(Lut, &x, FixedK, &fx);
|
||||
|
||||
// Compute error
|
||||
error = cmsDeltaE(&fx, &Goal);
|
||||
|
||||
// If not convergent, return last safe value
|
||||
if (error >= LastError)
|
||||
break;
|
||||
|
||||
// Keep latest values
|
||||
LastError = error;
|
||||
|
||||
ToEncoded(LastResult, &x);
|
||||
LastResult[3] = FixedK;
|
||||
|
||||
// Obtain slope
|
||||
ComputeJacobianLab(Lut, &Jacobian, &x, FixedK);
|
||||
|
||||
// Solve system
|
||||
tmp2.n[0] = fx.L - Goal.L;
|
||||
tmp2.n[1] = fx.a - Goal.a;
|
||||
tmp2.n[2] = fx.b - Goal.b;
|
||||
|
||||
if (!MAT3solve(&tmp, &Jacobian, &tmp2))
|
||||
break;
|
||||
|
||||
// Move our guess
|
||||
x.n[0] -= tmp.n[0];
|
||||
x.n[1] -= tmp.n[1];
|
||||
x.n[2] -= tmp.n[2];
|
||||
|
||||
// Some clipping....
|
||||
VEC3saturate(&x);
|
||||
}
|
||||
|
||||
Result[0] = LastResult[0];
|
||||
Result[1] = LastResult[1];
|
||||
Result[2] = LastResult[2];
|
||||
Result[3] = LastResult[3];
|
||||
|
||||
return LastError;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
// Shaper/Matrix handling
|
||||
// This routines handles the matrix-shaper method. A note about domain
|
||||
// is here required. If the shaper-matrix is invoked on INPUT profiles,
|
||||
// after the shaper process, we have a value between 0 and 0xFFFF. Thus,
|
||||
// for proper matrix handling, we must convert it to 15fix16, so
|
||||
// ToFixedDomain might be called. But cmsLinearInterpFixed() returns
|
||||
// data yet in fixed point, so no additional process is required.
|
||||
// Then, we obtain data on 15.16, so we need to shift >> by 1 to
|
||||
// obtain 1.15 PCS format.
|
||||
|
||||
// On OUTPUT profiles, things are inverse, we must first expand 1 bit
|
||||
// by shifting left, and then convert result between 0 and 1.000 to
|
||||
// RGB, so FromFixedDomain() must be called before pass values to
|
||||
// shaper. Trickly, there is a situation where this shifts works
|
||||
// little different. Sometimes, lcms smelts input/output
|
||||
// matrices into a single, one shaper, process. In such cases, since
|
||||
// input is encoded from 0 to 0xffff, we must first use the shaper and
|
||||
// then the matrix, an additional FromFixedDomain() must be used to
|
||||
// accomodate output values.
|
||||
|
||||
// For a sake of simplicity, I will handle this three behaviours
|
||||
// with different routines, so the flags MATSHAPER_INPUT and MATSHAPER_OUTPUT
|
||||
// can be conbined to signal smelted matrix-shapers
|
||||
|
||||
|
||||
|
||||
static
|
||||
int ComputeTables(LPGAMMATABLE Table[3], LPWORD Out[3], LPL16PARAMS p16)
|
||||
{
|
||||
int i, AllLinear;
|
||||
|
||||
cmsCalcL16Params(Table[0] -> nEntries, p16);
|
||||
|
||||
AllLinear = 0;
|
||||
for (i=0; i < 3; i++)
|
||||
{
|
||||
LPWORD PtrW;
|
||||
|
||||
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * p16 -> nSamples);
|
||||
|
||||
if (PtrW == NULL) return -1; // Signal error
|
||||
|
||||
CopyMemory(PtrW, Table[i] -> GammaTable, sizeof(WORD) * Table[i] -> nEntries);
|
||||
|
||||
Out[i] = PtrW; // Set table pointer
|
||||
|
||||
// Linear after all?
|
||||
|
||||
AllLinear += cmsIsLinear(PtrW, p16 -> nSamples);
|
||||
}
|
||||
|
||||
// If is all linear, then supress table interpolation (this
|
||||
// will speed greately some trivial operations.
|
||||
// Return 1 if present, 0 if all linear
|
||||
|
||||
|
||||
if (AllLinear != 3) return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
LPMATSHAPER cmsAllocMatShaper2(LPMAT3 Matrix, LPGAMMATABLE In[], LPGAMMATABLE Out[], DWORD Behaviour)
|
||||
{
|
||||
LPMATSHAPER NewMatShaper;
|
||||
int rc;
|
||||
|
||||
NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER));
|
||||
if (NewMatShaper)
|
||||
ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
|
||||
|
||||
NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
|
||||
|
||||
// Fill matrix part
|
||||
|
||||
MAT3toFix(&NewMatShaper -> Matrix, Matrix);
|
||||
|
||||
// Reality check
|
||||
|
||||
if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
|
||||
NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
|
||||
|
||||
// Now, on the table characteristics
|
||||
|
||||
if (Out) {
|
||||
|
||||
rc = ComputeTables(Out, NewMatShaper ->L, &NewMatShaper ->p16);
|
||||
if (rc < 0) {
|
||||
cmsFreeMatShaper(NewMatShaper);
|
||||
return NULL;
|
||||
}
|
||||
if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
|
||||
}
|
||||
|
||||
|
||||
if (In) {
|
||||
|
||||
rc = ComputeTables(In, NewMatShaper ->L2, &NewMatShaper ->p2_16);
|
||||
if (rc < 0) {
|
||||
cmsFreeMatShaper(NewMatShaper);
|
||||
return NULL;
|
||||
}
|
||||
if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASINPSHAPER;
|
||||
}
|
||||
|
||||
|
||||
return NewMatShaper;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Creation & Destruction
|
||||
|
||||
LPMATSHAPER cmsAllocMatShaper(LPMAT3 Matrix, LPGAMMATABLE Tables[], DWORD Behaviour)
|
||||
{
|
||||
LPMATSHAPER NewMatShaper;
|
||||
int i, AllLinear;
|
||||
|
||||
NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER));
|
||||
if (NewMatShaper)
|
||||
ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
|
||||
|
||||
NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
|
||||
|
||||
// Fill matrix part
|
||||
|
||||
MAT3toFix(&NewMatShaper -> Matrix, Matrix);
|
||||
|
||||
// Reality check
|
||||
|
||||
if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
|
||||
NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
|
||||
|
||||
// Now, on the table characteristics
|
||||
|
||||
cmsCalcL16Params(Tables[0] -> nEntries, &NewMatShaper -> p16);
|
||||
|
||||
// Copy tables
|
||||
|
||||
AllLinear = 0;
|
||||
for (i=0; i < 3; i++)
|
||||
{
|
||||
LPWORD PtrW;
|
||||
|
||||
PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewMatShaper -> p16.nSamples);
|
||||
|
||||
if (PtrW == NULL) {
|
||||
cmsFreeMatShaper(NewMatShaper);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CopyMemory(PtrW, Tables[i] -> GammaTable,
|
||||
sizeof(WORD) * Tables[i] -> nEntries);
|
||||
|
||||
NewMatShaper -> L[i] = PtrW; // Set table pointer
|
||||
|
||||
// Linear after all?
|
||||
|
||||
AllLinear += cmsIsLinear(PtrW, NewMatShaper -> p16.nSamples);
|
||||
}
|
||||
|
||||
// If is all linear, then supress table interpolation (this
|
||||
// will speed greately some trivial operations
|
||||
|
||||
if (AllLinear != 3)
|
||||
NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
|
||||
|
||||
return NewMatShaper;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Free associated memory
|
||||
|
||||
void cmsFreeMatShaper(LPMATSHAPER MatShaper)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!MatShaper) return;
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
{
|
||||
if (MatShaper -> L[i]) free(MatShaper ->L[i]);
|
||||
if (MatShaper -> L2[i]) free(MatShaper ->L2[i]);
|
||||
}
|
||||
|
||||
free(MatShaper);
|
||||
}
|
||||
|
||||
|
||||
// All smelted must postpose gamma to last stage.
|
||||
|
||||
static
|
||||
void AllSmeltedBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
|
||||
{
|
||||
|
||||
WORD tmp[3];
|
||||
WVEC3 InVect, OutVect;
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASINPSHAPER)
|
||||
{
|
||||
InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L2[0], &MatShaper -> p2_16);
|
||||
InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L2[1], &MatShaper -> p2_16);
|
||||
InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L2[2], &MatShaper -> p2_16);
|
||||
}
|
||||
else
|
||||
{
|
||||
InVect.n[VX] = ToFixedDomain(In[0]);
|
||||
InVect.n[VY] = ToFixedDomain(In[1]);
|
||||
InVect.n[VZ] = ToFixedDomain(In[2]);
|
||||
}
|
||||
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
|
||||
{
|
||||
|
||||
MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
|
||||
}
|
||||
else {
|
||||
|
||||
OutVect.n[VX] = InVect.n[VX];
|
||||
OutVect.n[VY] = InVect.n[VY];
|
||||
OutVect.n[VZ] = InVect.n[VZ];
|
||||
}
|
||||
|
||||
|
||||
tmp[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
|
||||
tmp[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
|
||||
tmp[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
|
||||
|
||||
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
|
||||
{
|
||||
Out[0] = cmsLinearInterpLUT16(tmp[0], MatShaper -> L[0], &MatShaper -> p16);
|
||||
Out[1] = cmsLinearInterpLUT16(tmp[1], MatShaper -> L[1], &MatShaper -> p16);
|
||||
Out[2] = cmsLinearInterpLUT16(tmp[2], MatShaper -> L[2], &MatShaper -> p16);
|
||||
}
|
||||
else
|
||||
{
|
||||
Out[0] = tmp[0];
|
||||
Out[1] = tmp[1];
|
||||
Out[2] = tmp[2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void InputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
|
||||
{
|
||||
WVEC3 InVect, OutVect;
|
||||
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
|
||||
{
|
||||
InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L[0], &MatShaper -> p16);
|
||||
InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L[1], &MatShaper -> p16);
|
||||
InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L[2], &MatShaper -> p16);
|
||||
}
|
||||
else
|
||||
{
|
||||
InVect.n[VX] = ToFixedDomain(In[0]);
|
||||
InVect.n[VY] = ToFixedDomain(In[1]);
|
||||
InVect.n[VZ] = ToFixedDomain(In[2]);
|
||||
}
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
|
||||
{
|
||||
MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutVect = InVect;
|
||||
}
|
||||
|
||||
// PCS in 1Fixed15 format, adjusting
|
||||
|
||||
Out[0] = _cmsClampWord((OutVect.n[VX]) >> 1);
|
||||
Out[1] = _cmsClampWord((OutVect.n[VY]) >> 1);
|
||||
Out[2] = _cmsClampWord((OutVect.n[VZ]) >> 1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void OutputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
|
||||
{
|
||||
WVEC3 InVect, OutVect;
|
||||
int i;
|
||||
|
||||
// We need to convert from XYZ to RGB, here we must
|
||||
// shift << 1 to pass between 1.15 to 15.16 formats
|
||||
|
||||
InVect.n[VX] = (Fixed32) In[0] << 1;
|
||||
InVect.n[VY] = (Fixed32) In[1] << 1;
|
||||
InVect.n[VZ] = (Fixed32) In[2] << 1;
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
|
||||
{
|
||||
MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutVect = InVect;
|
||||
}
|
||||
|
||||
|
||||
if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
|
||||
{
|
||||
for (i=0; i < 3; i++)
|
||||
{
|
||||
|
||||
Out[i] = cmsLinearInterpLUT16(
|
||||
_cmsClampWord(FromFixedDomain(OutVect.n[i])),
|
||||
MatShaper -> L[i],
|
||||
&MatShaper ->p16);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Result from fixed domain to RGB
|
||||
|
||||
Out[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
|
||||
Out[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
|
||||
Out[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Master on evaluating shapers, 3 different behaviours
|
||||
|
||||
void cmsEvalMatShaper(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
|
||||
{
|
||||
|
||||
if ((MatShaper -> dwFlags & MATSHAPER_ALLSMELTED) == MATSHAPER_ALLSMELTED)
|
||||
{
|
||||
AllSmeltedBehaviour(MatShaper, In, Out);
|
||||
return;
|
||||
}
|
||||
if (MatShaper -> dwFlags & MATSHAPER_INPUT)
|
||||
{
|
||||
InputBehaviour(MatShaper, In, Out);
|
||||
return;
|
||||
}
|
||||
|
||||
OutputBehaviour(MatShaper, In, Out);
|
||||
}
|
|
@ -0,0 +1,816 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// Vector & Matrix stuff
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
void cdecl VEC3init(LPVEC3 r, double x, double y, double z);
|
||||
void cdecl VEC3initF(LPWVEC3 r, double x, double y, double z);
|
||||
void cdecl VEC3toFix(LPWVEC3 r, LPVEC3 v);
|
||||
void cdecl VEC3scaleFix(LPWORD r, LPWVEC3 Scale);
|
||||
void cdecl VEC3swap(LPVEC3 a, LPVEC3 b);
|
||||
void cdecl VEC3divK(LPVEC3 r, LPVEC3 v, double d);
|
||||
void cdecl VEC3perK(LPVEC3 r, LPVEC3 v, double d);
|
||||
void cdecl VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b);
|
||||
void cdecl VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b);
|
||||
void cdecl VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d);
|
||||
void cdecl VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v);
|
||||
void cdecl VEC3saturate(LPVEC3 v);
|
||||
|
||||
double cdecl VEC3length(LPVEC3 a);
|
||||
double cdecl VEC3distance(LPVEC3 a, LPVEC3 b);
|
||||
|
||||
|
||||
void cdecl MAT3identity(LPMAT3 a);
|
||||
void cdecl MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b);
|
||||
int cdecl MAT3inverse(LPMAT3 a, LPMAT3 b);
|
||||
LCMSBOOL cdecl MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b);
|
||||
double cdecl MAT3det(LPMAT3 m);
|
||||
void cdecl MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v);
|
||||
void cdecl MAT3toFix(LPWMAT3 r, LPMAT3 v);
|
||||
void cdecl MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v);
|
||||
void cdecl MAT3perK(LPMAT3 r, LPMAT3 v, double d);
|
||||
void cdecl MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d);
|
||||
|
||||
// --------------------- Implementation ----------------------------
|
||||
|
||||
#define DSWAP(x, y) {double tmp = (x); (x)=(y); (y)=tmp;}
|
||||
|
||||
|
||||
|
||||
#ifdef USE_ASSEMBLER
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4033)
|
||||
#pragma warning(disable : 4035)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Fixed32 FixedMul(Fixed32 a, Fixed32 b)
|
||||
{
|
||||
ASM {
|
||||
|
||||
mov eax, ss:a
|
||||
mov edx, ss:b
|
||||
imul edx
|
||||
add eax, 0x8000
|
||||
adc edx, 0
|
||||
shrd eax, edx, 16
|
||||
|
||||
}
|
||||
|
||||
RET(_EAX);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Fixed32 FixedSquare(Fixed32 a)
|
||||
{
|
||||
ASM {
|
||||
pushf
|
||||
push edx
|
||||
mov eax, ss:a
|
||||
imul eax
|
||||
add eax, 0x8000
|
||||
adc edx, 0
|
||||
shrd eax, edx, 16
|
||||
sar eax, 16
|
||||
pop edx
|
||||
popf
|
||||
}
|
||||
|
||||
RET(_EAX);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Linear intERPolation
|
||||
// a * (h - l) >> 16 + l
|
||||
|
||||
Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h)
|
||||
{
|
||||
ASM {
|
||||
mov eax, dword ptr ss:h
|
||||
mov edx, dword ptr ss:l
|
||||
push edx
|
||||
mov ecx, dword ptr ss:a
|
||||
sub eax, edx
|
||||
imul ecx
|
||||
add eax, 0x8000
|
||||
adc edx, 0
|
||||
shrd eax, edx, 16
|
||||
pop edx
|
||||
add eax, edx
|
||||
}
|
||||
|
||||
RET(_EAX);
|
||||
}
|
||||
|
||||
|
||||
// a as word is scaled by s as float
|
||||
|
||||
WORD FixedScale(WORD a, Fixed32 s)
|
||||
{
|
||||
ASM {
|
||||
|
||||
xor eax,eax
|
||||
mov ax, ss:a // This is faster that movzx eax, ss:a
|
||||
sal eax, 16
|
||||
mov edx, ss:s
|
||||
mul edx
|
||||
add eax, 0x8000
|
||||
adc edx, 0
|
||||
mov eax, edx
|
||||
}
|
||||
|
||||
RET(_EAX);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4033)
|
||||
#pragma warning(default : 4035)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
|
||||
// These are floating point versions for compilers that doesn't
|
||||
// support asm at all. Use with care, since this will slow down
|
||||
// all operations
|
||||
|
||||
|
||||
Fixed32 FixedMul(Fixed32 a, Fixed32 b)
|
||||
{
|
||||
#ifdef USE_INT64
|
||||
LCMSULONGLONG l = (LCMSULONGLONG) (LCMSSLONGLONG) a * (LCMSULONGLONG) (LCMSSLONGLONG) b + (LCMSULONGLONG) 0x8000;
|
||||
l >>= 16;
|
||||
return (Fixed32) l;
|
||||
#else
|
||||
return DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(a) * FIXED_TO_DOUBLE(b));
|
||||
#endif
|
||||
}
|
||||
|
||||
Fixed32 FixedSquare(Fixed32 a)
|
||||
{
|
||||
return FixedMul(a, a);
|
||||
}
|
||||
|
||||
|
||||
Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h)
|
||||
{
|
||||
#ifdef USE_INT64
|
||||
|
||||
LCMSULONGLONG dif = (LCMSULONGLONG) (h - l) * a + 0x8000;
|
||||
dif = (dif >> 16) + l;
|
||||
return (Fixed32) (dif);
|
||||
#else
|
||||
double dif = h - l;
|
||||
|
||||
dif *= a;
|
||||
dif /= 65536.0;
|
||||
dif += l;
|
||||
|
||||
return (Fixed32) (dif + 0.5);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
WORD FixedScale(WORD a, Fixed32 s)
|
||||
{
|
||||
return (WORD) (a * FIXED_TO_DOUBLE(s));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef USE_INLINE
|
||||
|
||||
Fixed32 ToFixedDomain(int a)
|
||||
{
|
||||
return a + ((a + 0x7fff) / 0xffff);
|
||||
}
|
||||
|
||||
|
||||
int FromFixedDomain(Fixed32 a)
|
||||
{
|
||||
return a - ((a + 0x7fff) >> 16);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Initiate a vector (double version)
|
||||
|
||||
|
||||
void VEC3init(LPVEC3 r, double x, double y, double z)
|
||||
{
|
||||
r -> n[VX] = x;
|
||||
r -> n[VY] = y;
|
||||
r -> n[VZ] = z;
|
||||
}
|
||||
|
||||
// Init a vector (fixed version)
|
||||
|
||||
void VEC3initF(LPWVEC3 r, double x, double y, double z)
|
||||
{
|
||||
r -> n[VX] = DOUBLE_TO_FIXED(x);
|
||||
r -> n[VY] = DOUBLE_TO_FIXED(y);
|
||||
r -> n[VZ] = DOUBLE_TO_FIXED(z);
|
||||
}
|
||||
|
||||
|
||||
// Convert to fixed point encoding is 1.0 = 0xFFFF
|
||||
|
||||
void VEC3toFix(LPWVEC3 r, LPVEC3 v)
|
||||
{
|
||||
r -> n[VX] = DOUBLE_TO_FIXED(v -> n[VX]);
|
||||
r -> n[VY] = DOUBLE_TO_FIXED(v -> n[VY]);
|
||||
r -> n[VZ] = DOUBLE_TO_FIXED(v -> n[VZ]);
|
||||
}
|
||||
|
||||
// Convert from fixed point
|
||||
|
||||
void VEC3fromFix(LPVEC3 r, LPWVEC3 v)
|
||||
{
|
||||
r -> n[VX] = FIXED_TO_DOUBLE(v -> n[VX]);
|
||||
r -> n[VY] = FIXED_TO_DOUBLE(v -> n[VY]);
|
||||
r -> n[VZ] = FIXED_TO_DOUBLE(v -> n[VZ]);
|
||||
}
|
||||
|
||||
|
||||
// Swap two double vectors
|
||||
|
||||
void VEC3swap(LPVEC3 a, LPVEC3 b)
|
||||
{
|
||||
DSWAP(a-> n[VX], b-> n[VX]);
|
||||
DSWAP(a-> n[VY], b-> n[VY]);
|
||||
DSWAP(a-> n[VZ], b-> n[VZ]);
|
||||
}
|
||||
|
||||
// Divide a vector by a constant
|
||||
|
||||
void VEC3divK(LPVEC3 r, LPVEC3 v, double d)
|
||||
{
|
||||
double d_inv = 1./d;
|
||||
|
||||
r -> n[VX] = v -> n[VX] * d_inv;
|
||||
r -> n[VY] = v -> n[VY] * d_inv;
|
||||
r -> n[VZ] = v -> n[VZ] * d_inv;
|
||||
}
|
||||
|
||||
// Multiply by a constant
|
||||
|
||||
void VEC3perK(LPVEC3 r, LPVEC3 v, double d )
|
||||
{
|
||||
r -> n[VX] = v -> n[VX] * d;
|
||||
r -> n[VY] = v -> n[VY] * d;
|
||||
r -> n[VZ] = v -> n[VZ] * d;
|
||||
}
|
||||
|
||||
|
||||
void VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b)
|
||||
{
|
||||
r -> n[VX] = a->n[VX]*b->n[VX];
|
||||
r -> n[VY] = a->n[VY]*b->n[VY];
|
||||
r -> n[VZ] = a->n[VZ]*b->n[VZ];
|
||||
}
|
||||
|
||||
// Minus
|
||||
|
||||
|
||||
void VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b)
|
||||
{
|
||||
r -> n[VX] = a -> n[VX] - b -> n[VX];
|
||||
r -> n[VY] = a -> n[VY] - b -> n[VY];
|
||||
r -> n[VZ] = a -> n[VZ] - b -> n[VZ];
|
||||
}
|
||||
|
||||
|
||||
// Check id two vectors are the same, allowing tolerance
|
||||
|
||||
static
|
||||
LCMSBOOL RangeCheck(double l, double h, double v)
|
||||
{
|
||||
return (v >= l && v <= h);
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL VEC3equal(LPWVEC3 a, LPWVEC3 b, double Tolerance)
|
||||
{
|
||||
int i;
|
||||
double c;
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
{
|
||||
c = FIXED_TO_DOUBLE(a -> n[i]);
|
||||
if (!RangeCheck(c - Tolerance,
|
||||
c + Tolerance,
|
||||
FIXED_TO_DOUBLE(b->n[i]))) return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LCMSBOOL VEC3equalF(LPVEC3 a, LPVEC3 b, double Tolerance)
|
||||
{
|
||||
int i;
|
||||
double c;
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
{
|
||||
c = a -> n[i];
|
||||
if (!RangeCheck(c - Tolerance,
|
||||
c + Tolerance,
|
||||
b->n[i])) return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void VEC3scaleFix(LPWORD r, LPWVEC3 Scale)
|
||||
{
|
||||
if (Scale -> n[VX] == 0x00010000L &&
|
||||
Scale -> n[VY] == 0x00010000L &&
|
||||
Scale -> n[VZ] == 0x00010000L) return;
|
||||
|
||||
r[0] = (WORD) FixedScale(r[0], Scale -> n[VX]);
|
||||
r[1] = (WORD) FixedScale(r[1], Scale -> n[VY]);
|
||||
r[2] = (WORD) FixedScale(r[2], Scale -> n[VZ]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Vector cross product
|
||||
|
||||
void VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v)
|
||||
{
|
||||
|
||||
r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ];
|
||||
r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX];
|
||||
r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// The vector size
|
||||
|
||||
double VEC3length(LPVEC3 a)
|
||||
{
|
||||
return sqrt(a ->n[VX] * a ->n[VX] +
|
||||
a ->n[VY] * a ->n[VY] +
|
||||
a ->n[VZ] * a ->n[VZ]);
|
||||
}
|
||||
|
||||
|
||||
// Saturate a vector into 0..1.0 range
|
||||
|
||||
void VEC3saturate(LPVEC3 v)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < 3; i++) {
|
||||
if (v ->n[i] < 0)
|
||||
v ->n[i] = 0;
|
||||
else
|
||||
if (v ->n[i] > 1.0)
|
||||
v ->n[i] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Euclidean distance
|
||||
|
||||
double VEC3distance(LPVEC3 a, LPVEC3 b)
|
||||
{
|
||||
double d1 = a ->n[VX] - b ->n[VX];
|
||||
double d2 = a ->n[VY] - b ->n[VY];
|
||||
double d3 = a ->n[VZ] - b ->n[VZ];
|
||||
|
||||
return sqrt(d1*d1 + d2*d2 + d3*d3);
|
||||
}
|
||||
|
||||
|
||||
// Identity
|
||||
|
||||
|
||||
void MAT3identity(LPMAT3 a)
|
||||
{
|
||||
VEC3init(&a-> v[0], 1.0, 0.0, 0.0);
|
||||
VEC3init(&a-> v[1], 0.0, 1.0, 0.0);
|
||||
VEC3init(&a-> v[2], 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Check if matrix is Identity. Allow a tolerance as %
|
||||
|
||||
LCMSBOOL MAT3isIdentity(LPWMAT3 a, double Tolerance)
|
||||
{
|
||||
int i;
|
||||
MAT3 Idd;
|
||||
WMAT3 Idf;
|
||||
|
||||
MAT3identity(&Idd);
|
||||
MAT3toFix(&Idf, &Idd);
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
if (!VEC3equal(&a -> v[i], &Idf.v[i], Tolerance)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
// Multiply two matrices
|
||||
|
||||
|
||||
void MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b)
|
||||
{
|
||||
#define ROWCOL(i, j) \
|
||||
a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j]
|
||||
|
||||
VEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2));
|
||||
VEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2));
|
||||
VEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2));
|
||||
|
||||
#undef ROWCOL //(i, j)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Inverse of a matrix b = a^(-1)
|
||||
// Gauss-Jordan elimination with partial pivoting
|
||||
|
||||
int MAT3inverse(LPMAT3 a, LPMAT3 b)
|
||||
{
|
||||
register int i, j, max;
|
||||
|
||||
MAT3identity(b);
|
||||
|
||||
// Loop over cols of a from left to right, eliminating above and below diag
|
||||
for (j=0; j<3; j++) { // Find largest pivot in column j among rows j..2
|
||||
|
||||
max = j; // Row with largest pivot candidate
|
||||
for (i=j+1; i<3; i++)
|
||||
if (fabs(a -> v[i].n[j]) > fabs(a -> v[max].n[j]))
|
||||
max = i;
|
||||
|
||||
// Swap rows max and j in a and b to put pivot on diagonal
|
||||
|
||||
VEC3swap(&a -> v[max], &a -> v[j]);
|
||||
VEC3swap(&b -> v[max], &b -> v[j]);
|
||||
|
||||
// Scale row j to have a unit diagonal
|
||||
|
||||
if (a -> v[j].n[j]==0.)
|
||||
return -1; // singular matrix; can't invert
|
||||
|
||||
VEC3divK(&b-> v[j], &b -> v[j], a->v[j].n[j]);
|
||||
VEC3divK(&a-> v[j], &a -> v[j], a->v[j].n[j]);
|
||||
|
||||
// Eliminate off-diagonal elems in col j of a, doing identical ops to b
|
||||
for (i=0; i<3; i++)
|
||||
|
||||
if (i !=j) {
|
||||
VEC3 temp;
|
||||
|
||||
VEC3perK(&temp, &b -> v[j], a -> v[i].n[j]);
|
||||
VEC3minus(&b -> v[i], &b -> v[i], &temp);
|
||||
|
||||
VEC3perK(&temp, &a -> v[j], a -> v[i].n[j]);
|
||||
VEC3minus(&a -> v[i], &a -> v[i], &temp);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Solve a system in the form Ax = b
|
||||
|
||||
LCMSBOOL MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b)
|
||||
{
|
||||
MAT3 m, a_1;
|
||||
|
||||
CopyMemory(&m, a, sizeof(MAT3));
|
||||
|
||||
if (!MAT3inverse(&m, &a_1)) return FALSE; // Singular matrix
|
||||
|
||||
MAT3eval(x, &a_1, b);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// The determinant
|
||||
|
||||
double MAT3det(LPMAT3 m)
|
||||
{
|
||||
|
||||
double a1 = m ->v[VX].n[VX];
|
||||
double a2 = m ->v[VX].n[VY];
|
||||
double a3 = m ->v[VX].n[VZ];
|
||||
double b1 = m ->v[VY].n[VX];
|
||||
double b2 = m ->v[VY].n[VY];
|
||||
double b3 = m ->v[VY].n[VZ];
|
||||
double c1 = m ->v[VZ].n[VX];
|
||||
double c2 = m ->v[VZ].n[VY];
|
||||
double c3 = m ->v[VZ].n[VZ];
|
||||
|
||||
|
||||
return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 - a3*b1*c2 - a3*b2*c1;
|
||||
}
|
||||
|
||||
|
||||
// linear transform
|
||||
|
||||
|
||||
void MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v)
|
||||
{
|
||||
r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ];
|
||||
r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ];
|
||||
r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ];
|
||||
}
|
||||
|
||||
|
||||
// Ok, this is another bottleneck of performance.
|
||||
|
||||
|
||||
#ifdef USE_ASSEMBLER
|
||||
|
||||
// ecx:ebx is result in 64 bits format
|
||||
// edi points to matrix, esi points to input vector
|
||||
// since only 3 accesses are in output, this is a stack variable
|
||||
|
||||
|
||||
void MAT3evalW(LPWVEC3 r_, LPWMAT3 a_, LPWVEC3 v_)
|
||||
{
|
||||
|
||||
ASM {
|
||||
|
||||
|
||||
mov esi, dword ptr ss:v_
|
||||
mov edi, dword ptr ss:a_
|
||||
|
||||
// r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) +
|
||||
|
||||
mov eax,dword ptr [esi]
|
||||
mov edx,dword ptr [edi]
|
||||
imul edx
|
||||
mov ecx, eax
|
||||
mov ebx, edx
|
||||
|
||||
// FixedMul(a->v[0].n[1], v->n[1]) +
|
||||
|
||||
mov eax,dword ptr [esi+4]
|
||||
mov edx,dword ptr [edi+4]
|
||||
imul edx
|
||||
add ecx, eax
|
||||
adc ebx, edx
|
||||
|
||||
// FixedMul(a->v[0].n[2], v->n[2]);
|
||||
|
||||
mov eax,dword ptr [esi+8]
|
||||
mov edx,dword ptr [edi+8]
|
||||
imul edx
|
||||
add ecx, eax
|
||||
adc ebx, edx
|
||||
|
||||
// Back to Fixed 15.16
|
||||
|
||||
add ecx, 0x8000
|
||||
adc ebx, 0
|
||||
shrd ecx, ebx, 16
|
||||
|
||||
push edi
|
||||
mov edi, dword ptr ss:r_
|
||||
mov dword ptr [edi], ecx // r -> n[VX]
|
||||
pop edi
|
||||
|
||||
|
||||
|
||||
// 2nd row ***************************
|
||||
|
||||
// FixedMul(a->v[1].n[0], v->n[0])
|
||||
|
||||
mov eax,dword ptr [esi]
|
||||
mov edx,dword ptr [edi+12]
|
||||
imul edx
|
||||
mov ecx, eax
|
||||
mov ebx, edx
|
||||
|
||||
// FixedMul(a->v[1].n[1], v->n[1]) +
|
||||
|
||||
mov eax,dword ptr [esi+4]
|
||||
mov edx,dword ptr [edi+16]
|
||||
imul edx
|
||||
add ecx, eax
|
||||
adc ebx, edx
|
||||
|
||||
// FixedMul(a->v[1].n[2], v->n[2]);
|
||||
|
||||
mov eax,dword ptr [esi+8]
|
||||
mov edx,dword ptr [edi+20]
|
||||
imul edx
|
||||
add ecx, eax
|
||||
adc ebx, edx
|
||||
|
||||
add ecx, 0x8000
|
||||
adc ebx, 0
|
||||
shrd ecx, ebx, 16
|
||||
|
||||
push edi
|
||||
mov edi, dword ptr ss:r_
|
||||
mov dword ptr [edi+4], ecx // r -> n[VY]
|
||||
pop edi
|
||||
|
||||
// 3d row **************************
|
||||
|
||||
// r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) +
|
||||
|
||||
mov eax,dword ptr [esi]
|
||||
mov edx,dword ptr [edi+24]
|
||||
imul edx
|
||||
mov ecx, eax
|
||||
mov ebx, edx
|
||||
|
||||
// FixedMul(a->v[2].n[1], v->n[1]) +
|
||||
|
||||
mov eax,dword ptr [esi+4]
|
||||
mov edx,dword ptr [edi+28]
|
||||
imul edx
|
||||
add ecx, eax
|
||||
adc ebx, edx
|
||||
|
||||
// FixedMul(a->v[2].n[2], v->n[2]);
|
||||
|
||||
mov eax,dword ptr [esi+8]
|
||||
mov edx,dword ptr [edi+32]
|
||||
imul edx
|
||||
add ecx, eax
|
||||
adc ebx, edx
|
||||
|
||||
add ecx, 0x8000
|
||||
adc ebx, 0
|
||||
shrd ecx, ebx, 16
|
||||
|
||||
mov edi, dword ptr ss:r_
|
||||
mov dword ptr [edi+8], ecx // r -> n[VZ]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
#ifdef USE_FLOAT
|
||||
|
||||
void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v)
|
||||
{
|
||||
r->n[VX] = DOUBLE_TO_FIXED(
|
||||
FIXED_TO_DOUBLE(a->v[0].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
|
||||
FIXED_TO_DOUBLE(a->v[0].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
|
||||
FIXED_TO_DOUBLE(a->v[0].n[2]) * FIXED_TO_DOUBLE(v->n[2])
|
||||
);
|
||||
|
||||
r->n[VY] = DOUBLE_TO_FIXED(
|
||||
FIXED_TO_DOUBLE(a->v[1].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
|
||||
FIXED_TO_DOUBLE(a->v[1].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
|
||||
FIXED_TO_DOUBLE(a->v[1].n[2]) * FIXED_TO_DOUBLE(v->n[2])
|
||||
);
|
||||
|
||||
r->n[VZ] = DOUBLE_TO_FIXED(
|
||||
FIXED_TO_DOUBLE(a->v[2].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
|
||||
FIXED_TO_DOUBLE(a->v[2].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
|
||||
FIXED_TO_DOUBLE(a->v[2].n[2]) * FIXED_TO_DOUBLE(v->n[2])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v)
|
||||
{
|
||||
|
||||
#ifdef USE_INT64
|
||||
|
||||
LCMSULONGLONG l1 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[0] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[1] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[2] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
|
||||
|
||||
LCMSULONGLONG l2 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[0] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[1] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[2] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
|
||||
|
||||
LCMSULONGLONG l3 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[0] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[1] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[2] *
|
||||
(LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
|
||||
l1 >>= 16;
|
||||
l2 >>= 16;
|
||||
l3 >>= 16;
|
||||
|
||||
r->n[VX] = (Fixed32) l1;
|
||||
r->n[VY] = (Fixed32) l2;
|
||||
r->n[VZ] = (Fixed32) l3;
|
||||
|
||||
#else
|
||||
|
||||
// FIXME: Rounding should be done at very last stage. There is 1-Contone rounding error!
|
||||
|
||||
r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) +
|
||||
FixedMul(a->v[0].n[1], v->n[1]) +
|
||||
FixedMul(a->v[0].n[2], v->n[2]);
|
||||
|
||||
r->n[VY] = FixedMul(a->v[1].n[0], v->n[0]) +
|
||||
FixedMul(a->v[1].n[1], v->n[1]) +
|
||||
FixedMul(a->v[1].n[2], v->n[2]);
|
||||
|
||||
r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) +
|
||||
FixedMul(a->v[2].n[1], v->n[1]) +
|
||||
FixedMul(a->v[2].n[2], v->n[2]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
void MAT3perK(LPMAT3 r, LPMAT3 v, double d)
|
||||
{
|
||||
VEC3perK(&r -> v[0], &v -> v[0], d);
|
||||
VEC3perK(&r -> v[1], &v -> v[1], d);
|
||||
VEC3perK(&r -> v[2], &v -> v[2], d);
|
||||
}
|
||||
|
||||
|
||||
void MAT3toFix(LPWMAT3 r, LPMAT3 v)
|
||||
{
|
||||
VEC3toFix(&r -> v[0], &v -> v[0]);
|
||||
VEC3toFix(&r -> v[1], &v -> v[1]);
|
||||
VEC3toFix(&r -> v[2], &v -> v[2]);
|
||||
}
|
||||
|
||||
void MAT3fromFix(LPMAT3 r, LPWMAT3 v)
|
||||
{
|
||||
VEC3fromFix(&r -> v[0], &v -> v[0]);
|
||||
VEC3fromFix(&r -> v[1], &v -> v[1]);
|
||||
VEC3fromFix(&r -> v[2], &v -> v[2]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Scale v by d and store it in r giving INTEGER
|
||||
|
||||
void VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d)
|
||||
{
|
||||
r -> n[VX] = (int) floor(v -> n[VX] * d + .5);
|
||||
r -> n[VY] = (int) floor(v -> n[VY] * d + .5);
|
||||
r -> n[VZ] = (int) floor(v -> n[VZ] * d + .5);
|
||||
}
|
||||
|
||||
void MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d)
|
||||
{
|
||||
VEC3scaleAndCut(&r -> v[0], &v -> v[0], d);
|
||||
VEC3scaleAndCut(&r -> v[1], &v -> v[1], d);
|
||||
VEC3scaleAndCut(&r -> v[2], &v -> v[2], d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
// Named color support
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
|
||||
static
|
||||
LPcmsNAMEDCOLORLIST GrowNamedColorList(LPcmsNAMEDCOLORLIST v, int ByElements)
|
||||
{
|
||||
if (ByElements > v ->Allocated) {
|
||||
|
||||
LPcmsNAMEDCOLORLIST TheNewList;
|
||||
int NewElements;
|
||||
size_t size;
|
||||
|
||||
if (v ->Allocated == 0)
|
||||
NewElements = 64; // Initial guess
|
||||
else
|
||||
NewElements = v ->Allocated;
|
||||
|
||||
while (ByElements > NewElements)
|
||||
NewElements *= 2;
|
||||
|
||||
size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * NewElements);
|
||||
TheNewList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
|
||||
|
||||
|
||||
if (TheNewList == NULL) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory reallocating named color list");
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
ZeroMemory(TheNewList, size);
|
||||
CopyMemory(TheNewList, v, sizeof(cmsNAMEDCOLORLIST) + (v ->nColors - 1) * sizeof(cmsNAMEDCOLOR));
|
||||
TheNewList -> Allocated = NewElements;
|
||||
|
||||
free(v);
|
||||
return TheNewList;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
LPcmsNAMEDCOLORLIST cmsAllocNamedColorList(int n)
|
||||
{
|
||||
size_t size = sizeof(cmsNAMEDCOLORLIST) + (n - 1) * sizeof(cmsNAMEDCOLOR);
|
||||
|
||||
LPcmsNAMEDCOLORLIST v = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
|
||||
|
||||
|
||||
if (v == NULL) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory creating named color list");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZeroMemory(v, size);
|
||||
|
||||
v ->nColors = n;
|
||||
v ->Allocated = n;
|
||||
v ->Prefix[0] = 0;
|
||||
v ->Suffix[0] = 0;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void cmsFreeNamedColorList(LPcmsNAMEDCOLORLIST v)
|
||||
{
|
||||
if (v == NULL) {
|
||||
cmsSignalError(LCMS_ERRC_RECOVERABLE, "Couldn't free a NULL named color list");
|
||||
return;
|
||||
}
|
||||
|
||||
free(v);
|
||||
}
|
||||
|
||||
LCMSBOOL cmsAppendNamedColor(cmsHTRANSFORM xform, const char* Name, WORD PCS[3], WORD Colorant[MAXCHANNELS])
|
||||
{
|
||||
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
|
||||
LPcmsNAMEDCOLORLIST List;
|
||||
int i;
|
||||
|
||||
if (v ->NamedColorList == NULL) return FALSE;
|
||||
|
||||
v ->NamedColorList = GrowNamedColorList(v ->NamedColorList, v->NamedColorList ->nColors + 1);
|
||||
|
||||
List = v ->NamedColorList;
|
||||
|
||||
for (i=0; i < MAXCHANNELS; i++)
|
||||
List ->List[List ->nColors].DeviceColorant[i] = Colorant[i];
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
List ->List[List ->nColors].PCS[i] = PCS[i];
|
||||
|
||||
strncpy(List ->List[List ->nColors].Name, Name, MAX_PATH-1);
|
||||
|
||||
List ->nColors++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Returns named color count
|
||||
|
||||
int LCMSEXPORT cmsNamedColorCount(cmsHTRANSFORM xform)
|
||||
{
|
||||
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
|
||||
|
||||
if (v ->NamedColorList == NULL) return 0;
|
||||
return v ->NamedColorList ->nColors;
|
||||
}
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, char* Name, char* Prefix, char* Suffix)
|
||||
{
|
||||
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
|
||||
|
||||
|
||||
if (v ->NamedColorList == NULL) return FALSE;
|
||||
|
||||
if (nColor < 0 || nColor >= cmsNamedColorCount(xform)) return FALSE;
|
||||
|
||||
if (Name) strncpy(Name, v ->NamedColorList->List[nColor].Name, 31);
|
||||
if (Prefix) strncpy(Prefix, v ->NamedColorList->Prefix, 31);
|
||||
if (Suffix) strncpy(Suffix, v ->NamedColorList->Suffix, 31);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int LCMSEXPORT cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name)
|
||||
{
|
||||
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
|
||||
int i, n;
|
||||
|
||||
if (v ->NamedColorList == NULL) return -1;
|
||||
|
||||
n = cmsNamedColorCount(xform);
|
||||
for (i=0; i < n; i++) {
|
||||
if (stricmp(Name, v ->NamedColorList->List[i].Name) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,601 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// inter PCS conversions XYZ <-> CIE L* a* b*
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
/*
|
||||
|
||||
|
||||
CIE 15:2004 CIELab is defined as:
|
||||
|
||||
L* = 116*f(Y/Yn) - 16 0 <= L* <= 100
|
||||
a* = 500*[f(X/Xn) - f(Y/Yn)]
|
||||
b* = 200*[f(Y/Yn) - f(Z/Zn)]
|
||||
|
||||
and
|
||||
|
||||
f(t) = t^(1/3) 1 >= t > (24/116)^3
|
||||
(841/108)*t + (16/116) 0 <= t <= (24/116)^3
|
||||
|
||||
|
||||
Reverse transform is:
|
||||
|
||||
X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116)
|
||||
= Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116)
|
||||
|
||||
|
||||
|
||||
Following ICC. PCS in Lab is coded as:
|
||||
|
||||
8 bit Lab PCS:
|
||||
|
||||
L* 0..100 into a 0..ff byte.
|
||||
a* t + 128 range is -128.0 +127.0
|
||||
b*
|
||||
|
||||
16 bit Lab PCS:
|
||||
|
||||
L* 0..100 into a 0..ff00 word.
|
||||
a* t + 128 range is -128.0 +127.9961
|
||||
b*
|
||||
|
||||
|
||||
We are always playing with 16 bits-data, so I will ignore the
|
||||
8-bits encoding scheme.
|
||||
|
||||
|
||||
Interchange Space Component Actual Range Encoded Range
|
||||
CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff
|
||||
CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff
|
||||
CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff
|
||||
|
||||
Version 2,3
|
||||
-----------
|
||||
|
||||
CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00
|
||||
CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
|
||||
CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
|
||||
|
||||
|
||||
Version 4
|
||||
---------
|
||||
|
||||
CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff
|
||||
CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
|
||||
CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// On most modern computers, D > 4 M (i.e. a division takes more than 4
|
||||
// multiplications worth of time), so it is probably preferable to compute
|
||||
// a 24 bit result directly.
|
||||
|
||||
// #define ITERATE 1
|
||||
|
||||
static
|
||||
float CubeRoot(float x)
|
||||
{
|
||||
float fr, r;
|
||||
int ex, shx;
|
||||
|
||||
/* Argument reduction */
|
||||
fr = (float) frexp(x, &ex); /* separate into mantissa and exponent */
|
||||
shx = ex % 3;
|
||||
|
||||
if (shx > 0)
|
||||
shx -= 3; /* compute shx such that (ex - shx) is divisible by 3 */
|
||||
|
||||
ex = (ex - shx) / 3; /* exponent of cube root */
|
||||
fr = (float) ldexp(fr, shx);
|
||||
|
||||
/* 0.125 <= fr < 1.0 */
|
||||
|
||||
#ifdef ITERATE
|
||||
/* Compute seed with a quadratic approximation */
|
||||
|
||||
fr = (-0.46946116F * fr + 1.072302F) * fr + 0.3812513F;/* 0.5<=fr<1 */
|
||||
r = ldexp(fr, ex); /* 6 bits of precision */
|
||||
|
||||
/* Newton-Raphson iterations */
|
||||
|
||||
r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 12 bits */
|
||||
r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 24 bits */
|
||||
#else /* ITERATE */
|
||||
|
||||
/* Use quartic rational polynomial with error < 2^(-24) */
|
||||
|
||||
fr = (float) (((((45.2548339756803022511987494 * fr +
|
||||
192.2798368355061050458134625) * fr +
|
||||
119.1654824285581628956914143) * fr +
|
||||
13.43250139086239872172837314) * fr +
|
||||
0.1636161226585754240958355063)
|
||||
/
|
||||
((((14.80884093219134573786480845 * fr +
|
||||
151.9714051044435648658557668) * fr +
|
||||
168.5254414101568283957668343) * fr +
|
||||
33.9905941350215598754191872) * fr +
|
||||
1.0));
|
||||
r = (float) ldexp(fr, ex); /* 24 bits of precision */
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
static
|
||||
double f(double t)
|
||||
{
|
||||
|
||||
const double Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0);
|
||||
|
||||
if (t <= Limit)
|
||||
return (841.0/108.0) * t + (16.0/116.0);
|
||||
else
|
||||
return CubeRoot((float) t);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
double f_1(double t)
|
||||
{
|
||||
const double Limit = (24.0/116.0);
|
||||
|
||||
if (t <= Limit)
|
||||
{
|
||||
double tmp;
|
||||
|
||||
tmp = (108.0/841.0) * (t - (16.0/116.0));
|
||||
if (tmp <= 0.0) return 0.0;
|
||||
else return tmp;
|
||||
}
|
||||
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LCMSEXPORT cmsXYZ2Lab(LPcmsCIEXYZ WhitePoint, LPcmsCIELab Lab, const cmsCIEXYZ* xyz)
|
||||
{
|
||||
double fx, fy, fz;
|
||||
|
||||
if (xyz -> X == 0 && xyz -> Y == 0 && xyz -> Z == 0)
|
||||
{
|
||||
Lab -> L = 0;
|
||||
Lab -> a = 0;
|
||||
Lab -> b = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (WhitePoint == NULL)
|
||||
WhitePoint = cmsD50_XYZ();
|
||||
|
||||
fx = f(xyz->X / WhitePoint->X);
|
||||
fy = f(xyz->Y / WhitePoint->Y);
|
||||
fz = f(xyz->Z / WhitePoint->Z);
|
||||
|
||||
Lab->L = 116.0* fy - 16.;
|
||||
|
||||
Lab->a = 500.0*(fx - fy);
|
||||
Lab->b = 200.0*(fy - fz);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void cmsXYZ2LabEncoded(WORD XYZ[3], WORD Lab[3])
|
||||
{
|
||||
Fixed32 X, Y, Z;
|
||||
double x, y, z, L, a, b;
|
||||
double fx, fy, fz;
|
||||
Fixed32 wL, wa, wb;
|
||||
|
||||
X = (Fixed32) XYZ[0] << 1;
|
||||
Y = (Fixed32) XYZ[1] << 1;
|
||||
Z = (Fixed32) XYZ[2] << 1;
|
||||
|
||||
|
||||
if (X==0 && Y==0 && Z==0) {
|
||||
|
||||
Lab[0] = 0;
|
||||
Lab[1] = Lab[2] = 0x8000;
|
||||
return;
|
||||
}
|
||||
|
||||
// PCS is in D50
|
||||
|
||||
|
||||
x = FIXED_TO_DOUBLE(X) / D50X;
|
||||
y = FIXED_TO_DOUBLE(Y) / D50Y;
|
||||
z = FIXED_TO_DOUBLE(Z) / D50Z;
|
||||
|
||||
|
||||
fx = f(x);
|
||||
fy = f(y);
|
||||
fz = f(z);
|
||||
|
||||
L = 116.* fy - 16.;
|
||||
|
||||
a = 500.*(fx - fy);
|
||||
b = 200.*(fy - fz);
|
||||
|
||||
a += 128.;
|
||||
b += 128.;
|
||||
|
||||
wL = (int) (L * 652.800 + .5);
|
||||
wa = (int) (a * 256.0 + .5);
|
||||
wb = (int) (b * 256.0 + .5);
|
||||
|
||||
|
||||
Lab[0] = Clamp_L(wL);
|
||||
Lab[1] = Clamp_ab(wa);
|
||||
Lab[2] = Clamp_ab(wb);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void LCMSEXPORT cmsLab2XYZ(LPcmsCIEXYZ WhitePoint, LPcmsCIEXYZ xyz, const cmsCIELab* Lab)
|
||||
{
|
||||
double x, y, z;
|
||||
|
||||
if (Lab -> L <= 0) {
|
||||
xyz -> X = 0;
|
||||
xyz -> Y = 0;
|
||||
xyz -> Z = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (WhitePoint == NULL)
|
||||
WhitePoint = cmsD50_XYZ();
|
||||
|
||||
y = (Lab-> L + 16.0) / 116.0;
|
||||
x = y + 0.002 * Lab -> a;
|
||||
z = y - 0.005 * Lab -> b;
|
||||
|
||||
xyz -> X = f_1(x) * WhitePoint -> X;
|
||||
xyz -> Y = f_1(y) * WhitePoint -> Y;
|
||||
xyz -> Z = f_1(z) * WhitePoint -> Z;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void cmsLab2XYZEncoded(WORD Lab[3], WORD XYZ[3])
|
||||
{
|
||||
double L, a, b;
|
||||
double X, Y, Z, x, y, z;
|
||||
|
||||
|
||||
L = ((double) Lab[0] * 100.0) / 65280.0;
|
||||
if (L==0.0) {
|
||||
|
||||
XYZ[0] = 0; XYZ[1] = 0; XYZ[2] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
a = ((double) Lab[1] / 256.0) - 128.0;
|
||||
b = ((double) Lab[2] / 256.0) - 128.0;
|
||||
|
||||
y = (L + 16.) / 116.0;
|
||||
x = y + 0.002 * a;
|
||||
z = y - 0.005 * b;
|
||||
|
||||
X = f_1(x) * D50X;
|
||||
Y = f_1(y) * D50Y;
|
||||
Z = f_1(z) * D50Z;
|
||||
|
||||
// Convert to 1.15 fixed format PCS
|
||||
|
||||
|
||||
XYZ[0] = _cmsClampWord((int) floor(X * 32768.0 + 0.5));
|
||||
XYZ[1] = _cmsClampWord((int) floor(Y * 32768.0 + 0.5));
|
||||
XYZ[2] = _cmsClampWord((int) floor(Z * 32768.0 + 0.5));
|
||||
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
double L2float3(WORD v)
|
||||
{
|
||||
Fixed32 fix32;
|
||||
|
||||
fix32 = (Fixed32) v;
|
||||
return (double) fix32 / 652.800;
|
||||
}
|
||||
|
||||
|
||||
// the a/b part
|
||||
|
||||
static
|
||||
double ab2float3(WORD v)
|
||||
{
|
||||
Fixed32 fix32;
|
||||
|
||||
fix32 = (Fixed32) v;
|
||||
return ((double) fix32/256.0)-128.0;
|
||||
}
|
||||
|
||||
static
|
||||
WORD L2Fix3(double L)
|
||||
{
|
||||
return (WORD) (L * 652.800 + 0.5);
|
||||
}
|
||||
|
||||
static
|
||||
WORD ab2Fix3(double ab)
|
||||
{
|
||||
return (WORD) ((ab + 128.0) * 256.0 + 0.5);
|
||||
}
|
||||
|
||||
|
||||
// ICC 4.0 -- ICC has changed PCS Lab encoding.
|
||||
|
||||
static
|
||||
WORD L2Fix4(double L)
|
||||
{
|
||||
return (WORD) (L * 655.35 + 0.5);
|
||||
}
|
||||
|
||||
static
|
||||
WORD ab2Fix4(double ab)
|
||||
{
|
||||
return (WORD) ((ab + 128.0) * 257.0 + 0.5);
|
||||
}
|
||||
|
||||
static
|
||||
double L2float4(WORD v)
|
||||
{
|
||||
Fixed32 fix32;
|
||||
|
||||
fix32 = (Fixed32) v;
|
||||
return (double) fix32 / 655.35;
|
||||
}
|
||||
|
||||
|
||||
// the a/b part
|
||||
|
||||
static
|
||||
double ab2float4(WORD v)
|
||||
{
|
||||
Fixed32 fix32;
|
||||
|
||||
fix32 = (Fixed32) v;
|
||||
return ((double) fix32/257.0)-128.0;
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsLabEncoded2Float(LPcmsCIELab Lab, const WORD wLab[3])
|
||||
{
|
||||
Lab->L = L2float3(wLab[0]);
|
||||
Lab->a = ab2float3(wLab[1]);
|
||||
Lab->b = ab2float3(wLab[2]);
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsLabEncoded2Float4(LPcmsCIELab Lab, const WORD wLab[3])
|
||||
{
|
||||
Lab->L = L2float4(wLab[0]);
|
||||
Lab->a = ab2float4(wLab[1]);
|
||||
Lab->b = ab2float4(wLab[2]);
|
||||
}
|
||||
|
||||
static
|
||||
double Clamp_L_double(double L)
|
||||
{
|
||||
if (L < 0) L = 0;
|
||||
if (L > 100) L = 100;
|
||||
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
double Clamp_ab_double(double ab)
|
||||
{
|
||||
if (ab < -128) ab = -128.0;
|
||||
if (ab > +127.9961) ab = +127.9961;
|
||||
|
||||
return ab;
|
||||
}
|
||||
|
||||
void LCMSEXPORT cmsFloat2LabEncoded(WORD wLab[3], const cmsCIELab* fLab)
|
||||
{
|
||||
cmsCIELab Lab;
|
||||
|
||||
|
||||
Lab.L = Clamp_L_double(fLab ->L);
|
||||
Lab.a = Clamp_ab_double(fLab ->a);
|
||||
Lab.b = Clamp_ab_double(fLab ->b);
|
||||
|
||||
wLab[0] = L2Fix3(Lab.L);
|
||||
wLab[1] = ab2Fix3(Lab.a);
|
||||
wLab[2] = ab2Fix3(Lab.b);
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsFloat2LabEncoded4(WORD wLab[3], const cmsCIELab* fLab)
|
||||
{
|
||||
cmsCIELab Lab;
|
||||
|
||||
|
||||
Lab.L = fLab ->L;
|
||||
Lab.a = fLab ->a;
|
||||
Lab.b = fLab ->b;
|
||||
|
||||
|
||||
if (Lab.L < 0) Lab.L = 0;
|
||||
if (Lab.L > 100.) Lab.L = 100.;
|
||||
|
||||
if (Lab.a < -128.) Lab.a = -128.;
|
||||
if (Lab.a > 127.) Lab.a = 127.;
|
||||
if (Lab.b < -128.) Lab.b = -128.;
|
||||
if (Lab.b > 127.) Lab.b = 127.;
|
||||
|
||||
|
||||
wLab[0] = L2Fix4(Lab.L);
|
||||
wLab[1] = ab2Fix4(Lab.a);
|
||||
wLab[2] = ab2Fix4(Lab.b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LCMSEXPORT cmsLab2LCh(LPcmsCIELCh LCh, const cmsCIELab* Lab)
|
||||
{
|
||||
double a, b;
|
||||
|
||||
LCh -> L = Clamp_L_double(Lab -> L);
|
||||
|
||||
a = Clamp_ab_double(Lab -> a);
|
||||
b = Clamp_ab_double(Lab -> b);
|
||||
|
||||
LCh -> C = pow(a * a + b * b, 0.5);
|
||||
|
||||
if (a == 0 && b == 0)
|
||||
LCh -> h = 0;
|
||||
else
|
||||
LCh -> h = atan2(b, a);
|
||||
|
||||
|
||||
LCh -> h *= (180. / M_PI);
|
||||
|
||||
|
||||
while (LCh -> h >= 360.) // Not necessary, but included as a check.
|
||||
LCh -> h -= 360.;
|
||||
|
||||
while (LCh -> h < 0)
|
||||
LCh -> h += 360.;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LCMSEXPORT cmsLCh2Lab(LPcmsCIELab Lab, const cmsCIELCh* LCh)
|
||||
{
|
||||
|
||||
double h = (LCh -> h * M_PI) / 180.0;
|
||||
|
||||
Lab -> L = Clamp_L_double(LCh -> L);
|
||||
Lab -> a = Clamp_ab_double(LCh -> C * cos(h));
|
||||
Lab -> b = Clamp_ab_double(LCh -> C * sin(h));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// In XYZ All 3 components are encoded using 1.15 fixed point
|
||||
|
||||
static
|
||||
WORD XYZ2Fix(double d)
|
||||
{
|
||||
return (WORD) floor(d * 32768.0 + 0.5);
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* fXYZ)
|
||||
{
|
||||
cmsCIEXYZ xyz;
|
||||
|
||||
xyz.X = fXYZ -> X;
|
||||
xyz.Y = fXYZ -> Y;
|
||||
xyz.Z = fXYZ -> Z;
|
||||
|
||||
|
||||
// Clamp to encodeable values.
|
||||
// 1.99997 is reserved as out-of-gamut marker
|
||||
|
||||
|
||||
if (xyz.Y <= 0) {
|
||||
|
||||
xyz.X = 0;
|
||||
xyz.Y = 0;
|
||||
xyz.Z = 0;
|
||||
}
|
||||
|
||||
|
||||
if (xyz.X > 1.99996)
|
||||
xyz.X = 1.99996;
|
||||
|
||||
if (xyz.X < 0)
|
||||
xyz.X = 0;
|
||||
|
||||
if (xyz.Y > 1.99996)
|
||||
xyz.Y = 1.99996;
|
||||
|
||||
if (xyz.Y < 0)
|
||||
xyz.Y = 0;
|
||||
|
||||
|
||||
if (xyz.Z > 1.99996)
|
||||
xyz.Z = 1.99996;
|
||||
|
||||
if (xyz.Z < 0)
|
||||
xyz.Z = 0;
|
||||
|
||||
|
||||
|
||||
XYZ[0] = XYZ2Fix(xyz.X);
|
||||
XYZ[1] = XYZ2Fix(xyz.Y);
|
||||
XYZ[2] = XYZ2Fix(xyz.Z);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// To convert from Fixed 1.15 point to double
|
||||
|
||||
static
|
||||
double XYZ2float(WORD v)
|
||||
{
|
||||
Fixed32 fix32;
|
||||
|
||||
// From 1.15 to 15.16
|
||||
|
||||
fix32 = v << 1;
|
||||
|
||||
// From fixed 15.16 to double
|
||||
|
||||
return FIXED_TO_DOUBLE(fix32);
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsXYZEncoded2Float(LPcmsCIEXYZ fXYZ, const WORD XYZ[3])
|
||||
{
|
||||
|
||||
fXYZ -> X = XYZ2float(XYZ[0]);
|
||||
fXYZ -> Y = XYZ2float(XYZ[1]);
|
||||
fXYZ -> Z = XYZ2float(XYZ[2]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,663 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
static volatile int GlobalBlackPreservationStrategy = 0;
|
||||
|
||||
// Quantize a value 0 <= i < MaxSamples
|
||||
|
||||
WORD _cmsQuantizeVal(double i, int MaxSamples)
|
||||
{
|
||||
double x;
|
||||
|
||||
x = ((double) i * 65535.) / (double) (MaxSamples - 1);
|
||||
|
||||
return (WORD) floor(x + .5);
|
||||
}
|
||||
|
||||
|
||||
// Is a table linear?
|
||||
|
||||
int cmsIsLinear(WORD Table[], int nEntries)
|
||||
{
|
||||
register int i;
|
||||
int diff;
|
||||
|
||||
for (i=0; i < nEntries; i++) {
|
||||
|
||||
diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
|
||||
if (diff > 3)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// pow() restricted to integer
|
||||
|
||||
static
|
||||
int ipow(int base, int exp)
|
||||
{
|
||||
int res = base;
|
||||
|
||||
while (--exp)
|
||||
res *= base;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Given n, 0<=n<=clut^dim, returns the colorant.
|
||||
|
||||
static
|
||||
int ComponentOf(int n, int clut, int nColorant)
|
||||
{
|
||||
if (nColorant <= 0)
|
||||
return (n % clut);
|
||||
|
||||
n /= ipow(clut, nColorant);
|
||||
|
||||
return (n % clut);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This routine does a sweep on whole input space, and calls its callback
|
||||
// function on knots. returns TRUE if all ok, FALSE otherwise.
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags)
|
||||
{
|
||||
int i, t, nTotalPoints, Colorant, index;
|
||||
WORD In[MAXCHANNELS], Out[MAXCHANNELS];
|
||||
|
||||
nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan);
|
||||
|
||||
index = 0;
|
||||
for (i = 0; i < nTotalPoints; i++) {
|
||||
|
||||
for (t=0; t < (int) Lut -> InputChan; t++) {
|
||||
|
||||
Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 ));
|
||||
In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints);
|
||||
}
|
||||
|
||||
|
||||
if (dwFlags & SAMPLER_HASTL1) {
|
||||
|
||||
for (t=0; t < (int) Lut -> InputChan; t++)
|
||||
In[t] = cmsReverseLinearInterpLUT16(In[t],
|
||||
Lut -> L1[t],
|
||||
&Lut -> In16params);
|
||||
}
|
||||
|
||||
|
||||
// if (dwFlags & SAMPLER_INSPECT) {
|
||||
|
||||
for (t=0; t < (int) Lut -> OutputChan; t++)
|
||||
Out[t] = Lut->T[index + t];
|
||||
// }
|
||||
|
||||
|
||||
if (!Sampler(In, Out, Cargo))
|
||||
return FALSE;
|
||||
|
||||
if (!(dwFlags & SAMPLER_INSPECT)) {
|
||||
|
||||
if (dwFlags & SAMPLER_HASTL2) {
|
||||
|
||||
for (t=0; t < (int) Lut -> OutputChan; t++)
|
||||
Out[t] = cmsReverseLinearInterpLUT16(Out[t],
|
||||
Lut -> L2[t],
|
||||
&Lut -> Out16params);
|
||||
}
|
||||
|
||||
|
||||
for (t=0; t < (int) Lut -> OutputChan; t++)
|
||||
Lut->T[index + t] = Out[t];
|
||||
|
||||
}
|
||||
|
||||
index += Lut -> OutputChan;
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// choose reasonable resolution
|
||||
int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags)
|
||||
{
|
||||
int nChannels;
|
||||
|
||||
// Already specified?
|
||||
if (dwFlags & 0x00FF0000) {
|
||||
// Yes, grab'em
|
||||
return (dwFlags >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
nChannels = _cmsChannelsOf(Colorspace);
|
||||
|
||||
// HighResPrecalc is maximum resolution
|
||||
|
||||
if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
|
||||
|
||||
if (nChannels > 4)
|
||||
return 7; // 7 for Hifi
|
||||
|
||||
if (nChannels == 4) // 23 for CMYK
|
||||
return 23;
|
||||
|
||||
return 49; // 49 for RGB and others
|
||||
}
|
||||
|
||||
|
||||
// LowResPrecal is stripped resolution
|
||||
|
||||
if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
|
||||
|
||||
if (nChannels > 4)
|
||||
return 6; // 6 for Hifi
|
||||
|
||||
if (nChannels == 1)
|
||||
return 33; // For monochrome
|
||||
|
||||
return 17; // 17 for remaining
|
||||
}
|
||||
|
||||
// Default values
|
||||
|
||||
if (nChannels > 4)
|
||||
return 7; // 7 for Hifi
|
||||
|
||||
if (nChannels == 4)
|
||||
return 17; // 17 for CMYK
|
||||
|
||||
return 33; // 33 for RGB
|
||||
|
||||
}
|
||||
|
||||
// Sampler implemented by another transform. This is a clean way to
|
||||
// precalculate the devicelink 3D CLUT for almost any transform
|
||||
|
||||
static
|
||||
int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This routine does compute the devicelink CLUT containing whole
|
||||
// transform. Handles any channel number.
|
||||
|
||||
LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags)
|
||||
{
|
||||
_LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
|
||||
LPLUT Grid;
|
||||
int nGridPoints;
|
||||
DWORD dwFormatIn, dwFormatOut;
|
||||
DWORD SaveFormatIn, SaveFormatOut;
|
||||
int ChannelsIn, ChannelsOut;
|
||||
LPLUT SaveGamutLUT;
|
||||
|
||||
|
||||
// Remove any gamut checking
|
||||
SaveGamutLUT = p ->Gamut;
|
||||
p ->Gamut = NULL;
|
||||
|
||||
ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace);
|
||||
ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace);
|
||||
|
||||
nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
|
||||
|
||||
Grid = cmsAllocLUT();
|
||||
if (!Grid) return NULL;
|
||||
|
||||
Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut);
|
||||
|
||||
// Compute device link on 16-bit basis
|
||||
dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2));
|
||||
dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2));
|
||||
|
||||
SaveFormatIn = p ->InputFormat;
|
||||
SaveFormatOut = p ->OutputFormat;
|
||||
|
||||
p -> InputFormat = dwFormatIn;
|
||||
p -> OutputFormat = dwFormatOut;
|
||||
p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn);
|
||||
p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut);
|
||||
|
||||
// Fix gamut & gamma possible mismatches.
|
||||
|
||||
if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) {
|
||||
|
||||
cmsHTRANSFORM hOne[1];
|
||||
hOne[0] = h;
|
||||
|
||||
_cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid);
|
||||
}
|
||||
|
||||
// Attention to this typecast! we can take the luxury to
|
||||
// do this since cmsHTRANSFORM is only an alias to a pointer
|
||||
// to the transform struct.
|
||||
|
||||
if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) {
|
||||
|
||||
cmsFreeLUT(Grid);
|
||||
Grid = NULL;
|
||||
}
|
||||
|
||||
p ->Gamut = SaveGamutLUT;
|
||||
p ->InputFormat = SaveFormatIn;
|
||||
p ->OutputFormat = SaveFormatOut;
|
||||
|
||||
return Grid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sampler for Black-preserving CMYK->CMYK transforms
|
||||
|
||||
typedef struct {
|
||||
cmsHTRANSFORM cmyk2cmyk;
|
||||
cmsHTRANSFORM cmyk2Lab;
|
||||
LPGAMMATABLE KTone;
|
||||
L16PARAMS KToneParams;
|
||||
LPLUT LabK2cmyk;
|
||||
double MaxError;
|
||||
|
||||
cmsHTRANSFORM hRoundTrip;
|
||||
int MaxTAC;
|
||||
|
||||
cmsHTRANSFORM hProofOutput;
|
||||
|
||||
} BPCARGO, *LPBPCARGO;
|
||||
|
||||
|
||||
|
||||
// Preserve black only if that is the only ink used
|
||||
static
|
||||
int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
BPCARGO* bp = (LPBPCARGO) Cargo;
|
||||
|
||||
// If going across black only, keep black only
|
||||
if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
|
||||
|
||||
// TAC does not apply because it is black ink!
|
||||
Out[0] = Out[1] = Out[2] = 0;
|
||||
Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Keep normal transform for other colors
|
||||
cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Preserve all K plane.
|
||||
static
|
||||
int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
|
||||
WORD LabK[4];
|
||||
double SumCMY, SumCMYK, Error;
|
||||
cmsCIELab ColorimetricLab, BlackPreservingLab;
|
||||
BPCARGO* bp = (LPBPCARGO) Cargo;
|
||||
|
||||
// Get the K across Tone curve
|
||||
LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
|
||||
|
||||
// If going across black only, keep black only
|
||||
if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
|
||||
|
||||
Out[0] = Out[1] = Out[2] = 0;
|
||||
Out[3] = LabK[3];
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Try the original transform, maybe K is already ok (valid on K=0)
|
||||
cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
|
||||
if (Out[3] == LabK[3]) return 1;
|
||||
|
||||
|
||||
// No, mesure and keep Lab measurement for further usage
|
||||
cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
|
||||
|
||||
// Is not black only and the transform doesn't keep black.
|
||||
// Obtain the Lab of CMYK. After that we have Lab + K
|
||||
cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1);
|
||||
|
||||
// Obtain the corresponding CMY using reverse interpolation.
|
||||
// As a seed, we use the colorimetric CMY
|
||||
cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out);
|
||||
|
||||
// Estimate the error
|
||||
cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
|
||||
Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
|
||||
|
||||
|
||||
// Apply TAC if needed
|
||||
|
||||
SumCMY = Out[0] + Out[1] + Out[2];
|
||||
SumCMYK = SumCMY + Out[3];
|
||||
|
||||
if (SumCMYK > bp ->MaxTAC) {
|
||||
|
||||
double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
|
||||
if (Ratio < 0)
|
||||
Ratio = 0;
|
||||
|
||||
Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C
|
||||
Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M
|
||||
Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Sample whole gamut to estimate maximum TAC
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4100)
|
||||
#endif
|
||||
|
||||
static
|
||||
int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
BPCARGO* bp = (LPBPCARGO) Cargo;
|
||||
WORD RoundTrip[4];
|
||||
int Sum;
|
||||
|
||||
cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
|
||||
|
||||
Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3];
|
||||
|
||||
if (Sum > bp ->MaxTAC)
|
||||
bp ->MaxTAC = Sum;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Estimate the maximum error
|
||||
static
|
||||
int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
BPCARGO* bp = (LPBPCARGO) Cargo;
|
||||
WORD ColorimetricOut[4];
|
||||
cmsCIELab ColorimetricLab, BlackPreservingLab;
|
||||
double Error;
|
||||
|
||||
if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1;
|
||||
|
||||
cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1);
|
||||
|
||||
cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1);
|
||||
cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
|
||||
|
||||
Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
|
||||
|
||||
if (Error > bp ->MaxError)
|
||||
bp ->MaxError = Error;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup the K preservation strategy
|
||||
int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n)
|
||||
{
|
||||
int OldVal = GlobalBlackPreservationStrategy;
|
||||
|
||||
if (n >= 0)
|
||||
GlobalBlackPreservationStrategy = n;
|
||||
|
||||
return OldVal;
|
||||
}
|
||||
|
||||
|
||||
// Get a pointer to callback on depending of strategy
|
||||
static
|
||||
_cmsSAMPLER _cmsGetBlackPreservationSampler(void)
|
||||
{
|
||||
switch (GlobalBlackPreservationStrategy) {
|
||||
|
||||
case 0: return BlackPreservingGrayOnlySampler;
|
||||
default: return BlackPreservingSampler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This is the black-preserving devicelink generator
|
||||
LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags)
|
||||
{
|
||||
_LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
|
||||
BPCARGO Cargo;
|
||||
LPLUT Grid;
|
||||
DWORD LocalFlags;
|
||||
cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
|
||||
int nGridPoints;
|
||||
icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual
|
||||
icSigAToB1Tag, // Relative colorimetric
|
||||
icSigAToB2Tag, // Saturation
|
||||
icSigAToB1Tag }; // Absolute colorimetric
|
||||
// (Relative/WhitePoint)
|
||||
|
||||
nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
|
||||
|
||||
// Get a copy of inteserting flags for this kind of xform
|
||||
LocalFlags = cmsFLAGS_NOTPRECALC;
|
||||
if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)
|
||||
LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
|
||||
|
||||
// Fill in cargo struct
|
||||
Cargo.cmyk2cmyk = hCMYK2CMYK;
|
||||
|
||||
// Compute tone curve.
|
||||
Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256);
|
||||
if (Cargo.KTone == NULL) return NULL;
|
||||
cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams);
|
||||
|
||||
|
||||
// Create a CMYK->Lab "normal" transform on input, without K-preservation
|
||||
Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16,
|
||||
hLab, TYPE_Lab_16, p->Intent, LocalFlags);
|
||||
|
||||
// We are going to use the reverse of proof direction
|
||||
Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]);
|
||||
|
||||
// Is there any table available?
|
||||
if (Cargo.LabK2cmyk == NULL) {
|
||||
|
||||
Grid = NULL;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Setup a roundtrip on output profile for TAC estimation
|
||||
Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
|
||||
p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC);
|
||||
|
||||
|
||||
// Setup a proof CMYK->Lab on output
|
||||
Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
|
||||
hLab, TYPE_Lab_DBL, p->Intent, LocalFlags);
|
||||
|
||||
|
||||
// Create an empty LUT for holding K-preserving xform
|
||||
Grid = cmsAllocLUT();
|
||||
if (!Grid) goto Cleanup;
|
||||
|
||||
Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4);
|
||||
|
||||
// Setup formatters
|
||||
p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16);
|
||||
p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16);
|
||||
|
||||
|
||||
|
||||
// Step #1, estimate TAC
|
||||
Cargo.MaxTAC = 0;
|
||||
if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) {
|
||||
|
||||
cmsFreeLUT(Grid);
|
||||
Grid = NULL;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
|
||||
// Step #2, compute approximation
|
||||
if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) {
|
||||
|
||||
cmsFreeLUT(Grid);
|
||||
Grid = NULL;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Step #3, estimate error
|
||||
Cargo.MaxError = 0;
|
||||
cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT);
|
||||
|
||||
|
||||
Cleanup:
|
||||
|
||||
if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab);
|
||||
if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip);
|
||||
if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput);
|
||||
|
||||
if (hLab) cmsCloseProfile(hLab);
|
||||
if (Cargo.KTone) cmsFreeGamma(Cargo.KTone);
|
||||
if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk);
|
||||
|
||||
return Grid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Fix broken LUT. just to obtain other CMS compatibility
|
||||
|
||||
static
|
||||
void PatchLUT(LPLUT Grid, WORD At[], WORD Value[],
|
||||
int nChannelsOut, int nChannelsIn)
|
||||
{
|
||||
LPL16PARAMS p16 = &Grid -> CLut16params;
|
||||
double px, py, pz, pw;
|
||||
int x0, y0, z0, w0;
|
||||
int i, index;
|
||||
|
||||
|
||||
if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization
|
||||
|
||||
px = ((double) At[0] * (p16->Domain)) / 65535.0;
|
||||
py = ((double) At[1] * (p16->Domain)) / 65535.0;
|
||||
pz = ((double) At[2] * (p16->Domain)) / 65535.0;
|
||||
pw = ((double) At[3] * (p16->Domain)) / 65535.0;
|
||||
|
||||
x0 = (int) floor(px);
|
||||
y0 = (int) floor(py);
|
||||
z0 = (int) floor(pz);
|
||||
w0 = (int) floor(pw);
|
||||
|
||||
if (nChannelsIn == 4) {
|
||||
|
||||
if (((px - x0) != 0) ||
|
||||
((py - y0) != 0) ||
|
||||
((pz - z0) != 0) ||
|
||||
((pw - w0) != 0)) return; // Not on exact node
|
||||
|
||||
index = p16 -> opta4 * x0 +
|
||||
p16 -> opta3 * y0 +
|
||||
p16 -> opta2 * z0 +
|
||||
p16 -> opta1 * w0;
|
||||
}
|
||||
else
|
||||
if (nChannelsIn == 3) {
|
||||
|
||||
if (((px - x0) != 0) ||
|
||||
((py - y0) != 0) ||
|
||||
((pz - z0) != 0)) return; // Not on exact node
|
||||
|
||||
index = p16 -> opta3 * x0 +
|
||||
p16 -> opta2 * y0 +
|
||||
p16 -> opta1 * z0;
|
||||
}
|
||||
else
|
||||
if (nChannelsIn == 1) {
|
||||
|
||||
if (((px - x0) != 0)) return; // Not on exact node
|
||||
|
||||
index = p16 -> opta1 * x0;
|
||||
}
|
||||
else {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0; i < nChannelsOut; i++)
|
||||
Grid -> T[index + i] = Value[i];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
LCMSBOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p)
|
||||
{
|
||||
|
||||
WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut;
|
||||
int nOuts, nIns;
|
||||
|
||||
|
||||
if (!p -> DeviceLink) return FALSE;
|
||||
|
||||
if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE;
|
||||
if ((p ->PreviewProfile != NULL) &&
|
||||
(p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE;
|
||||
|
||||
|
||||
if (!_cmsEndPointsBySpace(p -> EntryColorSpace,
|
||||
&WhitePointIn, &BlackPointIn, &nIns)) return FALSE;
|
||||
|
||||
|
||||
if (!_cmsEndPointsBySpace(p -> ExitColorSpace,
|
||||
&WhitePointOut, &BlackPointOut, &nOuts)) return FALSE;
|
||||
|
||||
// Fix white only
|
||||
|
||||
PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns);
|
||||
// PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -0,0 +1,899 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
// Virtual (built-in) profiles
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// This function creates a profile based on White point, primaries and
|
||||
// transfer functions.
|
||||
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint,
|
||||
LPcmsCIExyYTRIPLE Primaries,
|
||||
LPGAMMATABLE TransferFunction[3])
|
||||
{
|
||||
cmsHPROFILE hICC;
|
||||
cmsCIEXYZ tmp;
|
||||
MAT3 MColorants;
|
||||
cmsCIEXYZTRIPLE Colorants;
|
||||
cmsCIExyY MaxWhite;
|
||||
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (!hICC) // can't allocate
|
||||
return NULL;
|
||||
|
||||
|
||||
cmsSetDeviceClass(hICC, icSigDisplayClass);
|
||||
cmsSetColorSpace(hICC, icSigRgbData);
|
||||
cmsSetPCS(hICC, icSigXYZData);
|
||||
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
|
||||
|
||||
|
||||
// Implement profile using following tags:
|
||||
//
|
||||
// 1 icSigProfileDescriptionTag
|
||||
// 2 icSigMediaWhitePointTag
|
||||
// 3 icSigRedColorantTag
|
||||
// 4 icSigGreenColorantTag
|
||||
// 5 icSigBlueColorantTag
|
||||
// 6 icSigRedTRCTag
|
||||
// 7 icSigGreenTRCTag
|
||||
// 8 icSigBlueTRCTag
|
||||
|
||||
// This conforms a standard RGB DisplayProfile as says ICC, and then I add
|
||||
|
||||
// 9 icSigChromaticityTag
|
||||
|
||||
// As addendum II
|
||||
|
||||
|
||||
// Fill-in the tags
|
||||
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms RGB virtual profile");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "rgb built-in");
|
||||
|
||||
|
||||
if (WhitePoint) {
|
||||
|
||||
cmsxyY2XYZ(&tmp, WhitePoint);
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp);
|
||||
}
|
||||
|
||||
if (WhitePoint && Primaries) {
|
||||
|
||||
MaxWhite.x = WhitePoint -> x;
|
||||
MaxWhite.y = WhitePoint -> y;
|
||||
MaxWhite.Y = 1.0;
|
||||
|
||||
if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries))
|
||||
{
|
||||
cmsCloseProfile(hICC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmsAdaptMatrixToD50(&MColorants, &MaxWhite);
|
||||
|
||||
Colorants.Red.X = MColorants.v[0].n[0];
|
||||
Colorants.Red.Y = MColorants.v[1].n[0];
|
||||
Colorants.Red.Z = MColorants.v[2].n[0];
|
||||
|
||||
Colorants.Green.X = MColorants.v[0].n[1];
|
||||
Colorants.Green.Y = MColorants.v[1].n[1];
|
||||
Colorants.Green.Z = MColorants.v[2].n[1];
|
||||
|
||||
Colorants.Blue.X = MColorants.v[0].n[2];
|
||||
Colorants.Blue.Y = MColorants.v[1].n[2];
|
||||
Colorants.Blue.Z = MColorants.v[2].n[2];
|
||||
|
||||
cmsAddTag(hICC, icSigRedColorantTag, (LPVOID) &Colorants.Red);
|
||||
cmsAddTag(hICC, icSigBlueColorantTag, (LPVOID) &Colorants.Blue);
|
||||
cmsAddTag(hICC, icSigGreenColorantTag, (LPVOID) &Colorants.Green);
|
||||
}
|
||||
|
||||
|
||||
if (TransferFunction) {
|
||||
|
||||
// In case of gamma, we must dup' the table pointer
|
||||
|
||||
cmsAddTag(hICC, icSigRedTRCTag, (LPVOID) TransferFunction[0]);
|
||||
cmsAddTag(hICC, icSigGreenTRCTag, (LPVOID) TransferFunction[1]);
|
||||
cmsAddTag(hICC, icSigBlueTRCTag, (LPVOID) TransferFunction[2]);
|
||||
}
|
||||
|
||||
if (Primaries) {
|
||||
cmsAddTag(hICC, icSigChromaticityTag, (LPVOID) Primaries);
|
||||
}
|
||||
|
||||
return hICC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This function creates a profile based on White point and transfer function.
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateGrayProfile(LPcmsCIExyY WhitePoint,
|
||||
LPGAMMATABLE TransferFunction)
|
||||
{
|
||||
cmsHPROFILE hICC;
|
||||
cmsCIEXYZ tmp;
|
||||
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (!hICC) // can't allocate
|
||||
return NULL;
|
||||
|
||||
|
||||
cmsSetDeviceClass(hICC, icSigDisplayClass);
|
||||
cmsSetColorSpace(hICC, icSigGrayData);
|
||||
cmsSetPCS(hICC, icSigXYZData);
|
||||
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
|
||||
|
||||
|
||||
|
||||
// Implement profile using following tags:
|
||||
//
|
||||
// 1 icSigProfileDescriptionTag
|
||||
// 2 icSigMediaWhitePointTag
|
||||
// 6 icSigGrayTRCTag
|
||||
|
||||
// This conforms a standard Gray DisplayProfile
|
||||
|
||||
// Fill-in the tags
|
||||
|
||||
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms gray virtual profile");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "gray built-in");
|
||||
|
||||
|
||||
if (WhitePoint) {
|
||||
|
||||
cmsxyY2XYZ(&tmp, WhitePoint);
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp);
|
||||
}
|
||||
|
||||
|
||||
if (TransferFunction) {
|
||||
|
||||
// In case of gamma, we must dup' the table pointer
|
||||
|
||||
cmsAddTag(hICC, icSigGrayTRCTag, (LPVOID) TransferFunction);
|
||||
}
|
||||
|
||||
return hICC;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int IsPCS(icColorSpaceSignature ColorSpace)
|
||||
{
|
||||
return (ColorSpace == icSigXYZData ||
|
||||
ColorSpace == icSigLabData);
|
||||
}
|
||||
|
||||
static
|
||||
void FixColorSpaces(cmsHPROFILE hProfile,
|
||||
icColorSpaceSignature ColorSpace,
|
||||
icColorSpaceSignature PCS,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
|
||||
if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
|
||||
|
||||
if (IsPCS(ColorSpace) && IsPCS(PCS)) {
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigAbstractClass);
|
||||
cmsSetColorSpace(hProfile, ColorSpace);
|
||||
cmsSetPCS(hProfile, PCS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigOutputClass);
|
||||
cmsSetPCS(hProfile, ColorSpace);
|
||||
cmsSetColorSpace(hProfile, PCS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigInputClass);
|
||||
cmsSetColorSpace(hProfile, ColorSpace);
|
||||
cmsSetPCS(hProfile, PCS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigLinkClass);
|
||||
cmsSetColorSpace(hProfile, ColorSpace);
|
||||
cmsSetPCS(hProfile, PCS);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
|
||||
{
|
||||
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
|
||||
cmsHPROFILE hICC;
|
||||
cmsCIEXYZ WhitePoint;
|
||||
int i, nColors;
|
||||
size_t Size;
|
||||
LPcmsNAMEDCOLORLIST nc2;
|
||||
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (hICC == NULL) return NULL;
|
||||
|
||||
cmsSetRenderingIntent(hICC, v -> Intent);
|
||||
cmsSetDeviceClass(hICC, icSigNamedColorClass);
|
||||
cmsSetColorSpace(hICC, v ->ExitColorSpace);
|
||||
cmsSetPCS(hICC, cmsGetPCS(v ->InputProfile));
|
||||
cmsTakeMediaWhitePoint(&WhitePoint, v ->InputProfile);
|
||||
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, &WhitePoint);
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Named color Device link");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Named color Device link");
|
||||
|
||||
|
||||
nColors = cmsNamedColorCount(xform);
|
||||
nc2 = cmsAllocNamedColorList(nColors);
|
||||
|
||||
Size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * (nColors-1));
|
||||
|
||||
CopyMemory(nc2, v->NamedColorList, Size);
|
||||
nc2 ->ColorantCount = _cmsChannelsOf(v ->ExitColorSpace);
|
||||
|
||||
for (i=0; i < nColors; i++) {
|
||||
cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
|
||||
}
|
||||
|
||||
cmsAddTag(hICC, icSigNamedColor2Tag, (void*) nc2);
|
||||
cmsFreeNamedColorList(nc2);
|
||||
|
||||
return hICC;
|
||||
}
|
||||
|
||||
|
||||
// Does convert a transform into a device link profile
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, DWORD dwFlags)
|
||||
{
|
||||
cmsHPROFILE hICC;
|
||||
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) hTransform;
|
||||
LPLUT Lut;
|
||||
LCMSBOOL MustFreeLUT;
|
||||
LPcmsNAMEDCOLORLIST InputColorant = NULL;
|
||||
LPcmsNAMEDCOLORLIST OutputColorant = NULL;
|
||||
|
||||
|
||||
// Check if is a named color transform
|
||||
|
||||
if (cmsGetDeviceClass(v ->InputProfile) == icSigNamedColorClass) {
|
||||
|
||||
return CreateNamedColorDevicelink(hTransform);
|
||||
|
||||
}
|
||||
|
||||
if (v ->DeviceLink) {
|
||||
|
||||
Lut = v -> DeviceLink;
|
||||
MustFreeLUT = FALSE;
|
||||
}
|
||||
else {
|
||||
|
||||
Lut = _cmsPrecalculateDeviceLink(hTransform, dwFlags);
|
||||
if (!Lut) return NULL;
|
||||
MustFreeLUT = TRUE;
|
||||
}
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (!hICC) { // can't allocate
|
||||
|
||||
if (MustFreeLUT) cmsFreeLUT(Lut);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FixColorSpaces(hICC, v -> EntryColorSpace, v -> ExitColorSpace, dwFlags);
|
||||
|
||||
cmsSetRenderingIntent(hICC, v -> Intent);
|
||||
|
||||
// Implement devicelink profile using following tags:
|
||||
//
|
||||
// 1 icSigProfileDescriptionTag
|
||||
// 2 icSigMediaWhitePointTag
|
||||
// 3 icSigAToB0Tag
|
||||
|
||||
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Device link");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Device link");
|
||||
|
||||
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
|
||||
|
||||
if (cmsGetDeviceClass(hICC) == icSigOutputClass) {
|
||||
|
||||
cmsAddTag(hICC, icSigBToA0Tag, (LPVOID) Lut);
|
||||
}
|
||||
else
|
||||
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
|
||||
|
||||
|
||||
|
||||
// Try to read input and output colorant table
|
||||
if (cmsIsTag(v ->InputProfile, icSigColorantTableTag)) {
|
||||
|
||||
// Input table can only come in this way.
|
||||
InputColorant = cmsReadColorantTable(v ->InputProfile, icSigColorantTableTag);
|
||||
}
|
||||
|
||||
// Output is a little bit more complex.
|
||||
if (cmsGetDeviceClass(v ->OutputProfile) == icSigLinkClass) {
|
||||
|
||||
// This tag may exist only on devicelink profiles.
|
||||
if (cmsIsTag(v ->OutputProfile, icSigColorantTableOutTag)) {
|
||||
|
||||
OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableOutTag);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (cmsIsTag(v ->OutputProfile, icSigColorantTableTag)) {
|
||||
|
||||
OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableTag);
|
||||
}
|
||||
}
|
||||
|
||||
if (InputColorant)
|
||||
cmsAddTag(hICC, icSigColorantTableTag, InputColorant);
|
||||
|
||||
if (OutputColorant)
|
||||
cmsAddTag(hICC, icSigColorantTableOutTag, OutputColorant);
|
||||
|
||||
|
||||
|
||||
if (MustFreeLUT) cmsFreeLUT(Lut);
|
||||
if (InputColorant) cmsFreeNamedColorList(InputColorant);
|
||||
if (OutputColorant) cmsFreeNamedColorList(OutputColorant);
|
||||
|
||||
return hICC;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// This is a devicelink operating in the target colorspace with as many transfer
|
||||
// functions as components
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateLinearizationDeviceLink(icColorSpaceSignature ColorSpace,
|
||||
LPGAMMATABLE TransferFunctions[])
|
||||
{
|
||||
cmsHPROFILE hICC;
|
||||
LPLUT Lut;
|
||||
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (!hICC) // can't allocate
|
||||
return NULL;
|
||||
|
||||
|
||||
cmsSetDeviceClass(hICC, icSigLinkClass);
|
||||
cmsSetColorSpace(hICC, ColorSpace);
|
||||
cmsSetPCS(hICC, ColorSpace);
|
||||
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
|
||||
|
||||
|
||||
// Creates a LUT with prelinearization step only
|
||||
Lut = cmsAllocLUT();
|
||||
if (Lut == NULL) return NULL;
|
||||
|
||||
// Set up channels
|
||||
Lut ->InputChan = Lut ->OutputChan = _cmsChannelsOf(ColorSpace);
|
||||
|
||||
// Copy tables to LUT
|
||||
cmsAllocLinearTable(Lut, TransferFunctions, 1);
|
||||
|
||||
// Create tags
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms linearization device link");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "linearization built-in");
|
||||
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
|
||||
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
|
||||
|
||||
// LUT is already on virtual profile
|
||||
cmsFreeLUT(Lut);
|
||||
|
||||
// Ok, done
|
||||
return hICC;
|
||||
}
|
||||
|
||||
|
||||
// Ink-limiting algorithm
|
||||
//
|
||||
// Sum = C + M + Y + K
|
||||
// If Sum > InkLimit
|
||||
// Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
|
||||
// if Ratio <0
|
||||
// Ratio=0
|
||||
// endif
|
||||
// Else
|
||||
// Ratio=1
|
||||
// endif
|
||||
//
|
||||
// C = Ratio * C
|
||||
// M = Ratio * M
|
||||
// Y = Ratio * Y
|
||||
// K: Does not change
|
||||
|
||||
static
|
||||
int InkLimitingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
double InkLimit = *(double *) Cargo;
|
||||
double SumCMY, SumCMYK, Ratio;
|
||||
|
||||
InkLimit = (InkLimit * 655.35);
|
||||
|
||||
SumCMY = In[0] + In[1] + In[2];
|
||||
SumCMYK = SumCMY + In[3];
|
||||
|
||||
if (SumCMYK > InkLimit) {
|
||||
|
||||
Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
|
||||
if (Ratio < 0)
|
||||
Ratio = 0;
|
||||
}
|
||||
else Ratio = 1;
|
||||
|
||||
Out[0] = (WORD) floor(In[0] * Ratio + 0.5); // C
|
||||
Out[1] = (WORD) floor(In[1] * Ratio + 0.5); // M
|
||||
Out[2] = (WORD) floor(In[2] * Ratio + 0.5); // Y
|
||||
|
||||
Out[3] = In[3]; // K (untouched)
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This is a devicelink operating in CMYK for ink-limiting
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace,
|
||||
double Limit)
|
||||
{
|
||||
cmsHPROFILE hICC;
|
||||
LPLUT Lut;
|
||||
|
||||
if (ColorSpace != icSigCmykData) {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "InkLimiting: Only CMYK currently supported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (Limit < 0.0 || Limit > 400) {
|
||||
|
||||
cmsSignalError(LCMS_ERRC_WARNING, "InkLimiting: Limit should be between 0..400");
|
||||
if (Limit < 0) Limit = 0;
|
||||
if (Limit > 400) Limit = 400;
|
||||
|
||||
}
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (!hICC) // can't allocate
|
||||
return NULL;
|
||||
|
||||
|
||||
cmsSetDeviceClass(hICC, icSigLinkClass);
|
||||
cmsSetColorSpace(hICC, ColorSpace);
|
||||
cmsSetPCS(hICC, ColorSpace);
|
||||
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
|
||||
|
||||
|
||||
// Creates a LUT with 3D grid only
|
||||
Lut = cmsAllocLUT();
|
||||
if (Lut == NULL) {
|
||||
cmsCloseProfile(hICC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
cmsAlloc3DGrid(Lut, 17, _cmsChannelsOf(ColorSpace),
|
||||
_cmsChannelsOf(ColorSpace));
|
||||
|
||||
if (!cmsSample3DGrid(Lut, InkLimitingSampler, (LPVOID) &Limit, 0)) {
|
||||
|
||||
// Shouldn't reach here
|
||||
cmsFreeLUT(Lut);
|
||||
cmsCloseProfile(hICC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create tags
|
||||
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms ink limiting device link");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "ink limiting built-in");
|
||||
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
|
||||
|
||||
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
|
||||
|
||||
// LUT is already on virtual profile
|
||||
cmsFreeLUT(Lut);
|
||||
|
||||
// Ok, done
|
||||
return hICC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
LPLUT Create3x3EmptyLUT(void)
|
||||
{
|
||||
LPLUT AToB0 = cmsAllocLUT();
|
||||
if (AToB0 == NULL) return NULL;
|
||||
|
||||
AToB0 -> InputChan = AToB0 -> OutputChan = 3;
|
||||
return AToB0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Creates a fake Lab identity.
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateLabProfile(LPcmsCIExyY WhitePoint)
|
||||
{
|
||||
cmsHPROFILE hProfile;
|
||||
LPLUT Lut;
|
||||
|
||||
hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
|
||||
if (hProfile == NULL) return NULL;
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigAbstractClass);
|
||||
cmsSetColorSpace(hProfile, icSigLabData);
|
||||
cmsSetPCS(hProfile, icSigLabData);
|
||||
|
||||
cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity");
|
||||
cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab built-in");
|
||||
|
||||
|
||||
// An empty LUTs is all we need
|
||||
Lut = Create3x3EmptyLUT();
|
||||
if (Lut == NULL) {
|
||||
cmsCloseProfile(hProfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
|
||||
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
|
||||
|
||||
cmsFreeLUT(Lut);
|
||||
|
||||
return hProfile;
|
||||
}
|
||||
|
||||
|
||||
// Creates a fake Lab identity.
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateLab4Profile(LPcmsCIExyY WhitePoint)
|
||||
{
|
||||
cmsHPROFILE hProfile;
|
||||
LPLUT Lut;
|
||||
|
||||
hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
|
||||
if (hProfile == NULL) return NULL;
|
||||
|
||||
cmsSetProfileICCversion(hProfile, 0x4000000);
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigAbstractClass);
|
||||
cmsSetColorSpace(hProfile, icSigLabData);
|
||||
cmsSetPCS(hProfile, icSigLabData);
|
||||
|
||||
cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity v4");
|
||||
cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab v4 built-in");
|
||||
|
||||
|
||||
// An empty LUTs is all we need
|
||||
Lut = Create3x3EmptyLUT();
|
||||
if (Lut == NULL) {
|
||||
cmsCloseProfile(hProfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Lut -> wFlags |= LUT_V4_INPUT_EMULATE_V2;
|
||||
cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
|
||||
|
||||
Lut -> wFlags |= LUT_V4_OUTPUT_EMULATE_V2;
|
||||
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
|
||||
|
||||
cmsFreeLUT(Lut);
|
||||
|
||||
return hProfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Creates a fake XYZ identity
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateXYZProfile(void)
|
||||
{
|
||||
cmsHPROFILE hProfile;
|
||||
LPLUT Lut;
|
||||
|
||||
hProfile = cmsCreateRGBProfile(cmsD50_xyY(), NULL, NULL);
|
||||
if (hProfile == NULL) return NULL;
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigAbstractClass);
|
||||
cmsSetColorSpace(hProfile, icSigXYZData);
|
||||
cmsSetPCS(hProfile, icSigXYZData);
|
||||
|
||||
cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms XYZ identity");
|
||||
cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "XYZ built-in");
|
||||
|
||||
// An empty LUTs is all we need
|
||||
Lut = Create3x3EmptyLUT();
|
||||
if (Lut == NULL) {
|
||||
cmsCloseProfile(hProfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
|
||||
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
|
||||
cmsAddTag(hProfile, icSigPreview0Tag, (LPVOID) Lut);
|
||||
|
||||
cmsFreeLUT(Lut);
|
||||
return hProfile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
If R’sRGB,G’sRGB, B’sRGB < 0.04045
|
||||
|
||||
R = R’sRGB / 12.92
|
||||
G = G’sRGB / 12.92
|
||||
B = B’sRGB / 12.92
|
||||
|
||||
|
||||
|
||||
else if R’sRGB,G’sRGB, B’sRGB >= 0.04045
|
||||
|
||||
R = ((R’sRGB + 0.055) / 1.055)^2.4
|
||||
G = ((G’sRGB + 0.055) / 1.055)^2.4
|
||||
B = ((B’sRGB + 0.055) / 1.055)^2.4
|
||||
|
||||
*/
|
||||
|
||||
static
|
||||
LPGAMMATABLE Build_sRGBGamma(void)
|
||||
{
|
||||
double Parameters[5];
|
||||
|
||||
Parameters[0] = 2.4;
|
||||
Parameters[1] = 1. / 1.055;
|
||||
Parameters[2] = 0.055 / 1.055;
|
||||
Parameters[3] = 1. / 12.92;
|
||||
Parameters[4] = 0.04045; // d
|
||||
|
||||
return cmsBuildParametricGamma(1024, 4, Parameters);
|
||||
}
|
||||
|
||||
// Create the ICC virtual profile for sRGB space
|
||||
cmsHPROFILE LCMSEXPORT cmsCreate_sRGBProfile(void)
|
||||
{
|
||||
cmsCIExyY D65;
|
||||
cmsCIExyYTRIPLE Rec709Primaries = {
|
||||
{0.6400, 0.3300, 1.0},
|
||||
{0.3000, 0.6000, 1.0},
|
||||
{0.1500, 0.0600, 1.0}
|
||||
};
|
||||
LPGAMMATABLE Gamma22[3];
|
||||
cmsHPROFILE hsRGB;
|
||||
|
||||
cmsWhitePointFromTemp(6504, &D65);
|
||||
Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma();
|
||||
|
||||
hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma22);
|
||||
cmsFreeGamma(Gamma22[0]);
|
||||
if (hsRGB == NULL) return NULL;
|
||||
|
||||
|
||||
cmsAddTag(hsRGB, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hsRGB, icSigDeviceModelDescTag, (LPVOID) "sRGB built-in");
|
||||
cmsAddTag(hsRGB, icSigProfileDescriptionTag, (LPVOID) "sRGB built-in");
|
||||
|
||||
return hsRGB;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
double Brightness;
|
||||
double Contrast;
|
||||
double Hue;
|
||||
double Saturation;
|
||||
cmsCIEXYZ WPsrc, WPdest;
|
||||
|
||||
} BCHSWADJUSTS, *LPBCHSWADJUSTS;
|
||||
|
||||
|
||||
static
|
||||
int bchswSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
|
||||
{
|
||||
cmsCIELab LabIn, LabOut;
|
||||
cmsCIELCh LChIn, LChOut;
|
||||
cmsCIEXYZ XYZ;
|
||||
LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
|
||||
|
||||
|
||||
cmsLabEncoded2Float(&LabIn, In);
|
||||
|
||||
|
||||
cmsLab2LCh(&LChIn, &LabIn);
|
||||
|
||||
// Do some adjusts on LCh
|
||||
|
||||
LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
|
||||
LChOut.C = LChIn.C + bchsw -> Saturation;
|
||||
LChOut.h = LChIn.h + bchsw -> Hue;
|
||||
|
||||
|
||||
cmsLCh2Lab(&LabOut, &LChOut);
|
||||
|
||||
// Move white point in Lab
|
||||
|
||||
cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut);
|
||||
cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ);
|
||||
|
||||
// Back to encoded
|
||||
|
||||
cmsFloat2LabEncoded(Out, &LabOut);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Creates an abstract profile operating in Lab space for Brightness,
|
||||
// contrast, Saturation and white point displacement
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
|
||||
double Bright,
|
||||
double Contrast,
|
||||
double Hue,
|
||||
double Saturation,
|
||||
int TempSrc,
|
||||
int TempDest)
|
||||
{
|
||||
cmsHPROFILE hICC;
|
||||
LPLUT Lut;
|
||||
BCHSWADJUSTS bchsw;
|
||||
cmsCIExyY WhitePnt;
|
||||
|
||||
bchsw.Brightness = Bright;
|
||||
bchsw.Contrast = Contrast;
|
||||
bchsw.Hue = Hue;
|
||||
bchsw.Saturation = Saturation;
|
||||
|
||||
cmsWhitePointFromTemp(TempSrc, &WhitePnt);
|
||||
cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
|
||||
|
||||
cmsWhitePointFromTemp(TempDest, &WhitePnt);
|
||||
cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
|
||||
|
||||
hICC = _cmsCreateProfilePlaceholder();
|
||||
if (!hICC) // can't allocate
|
||||
return NULL;
|
||||
|
||||
|
||||
cmsSetDeviceClass(hICC, icSigAbstractClass);
|
||||
cmsSetColorSpace(hICC, icSigLabData);
|
||||
cmsSetPCS(hICC, icSigLabData);
|
||||
|
||||
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
|
||||
|
||||
|
||||
// Creates a LUT with 3D grid only
|
||||
Lut = cmsAllocLUT();
|
||||
if (Lut == NULL) {
|
||||
cmsCloseProfile(hICC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmsAlloc3DGrid(Lut, nLUTPoints, 3, 3);
|
||||
|
||||
if (!cmsSample3DGrid(Lut, bchswSampler, (LPVOID) &bchsw, 0)) {
|
||||
|
||||
// Shouldn't reach here
|
||||
cmsFreeLUT(Lut);
|
||||
cmsCloseProfile(hICC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create tags
|
||||
|
||||
cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
|
||||
cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms BCHSW abstract profile");
|
||||
cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "BCHSW built-in");
|
||||
|
||||
cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
|
||||
|
||||
cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
|
||||
|
||||
// LUT is already on virtual profile
|
||||
cmsFreeLUT(Lut);
|
||||
|
||||
// Ok, done
|
||||
return hICC;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Creates a fake NULL profile. This profile return 1 channel as always 0.
|
||||
// Is useful only for gamut checking tricks
|
||||
|
||||
cmsHPROFILE LCMSEXPORT cmsCreateNULLProfile(void)
|
||||
{
|
||||
cmsHPROFILE hProfile;
|
||||
LPLUT Lut;
|
||||
LPGAMMATABLE EmptyTab;
|
||||
|
||||
hProfile = _cmsCreateProfilePlaceholder();
|
||||
if (!hProfile) // can't allocate
|
||||
return NULL;
|
||||
|
||||
cmsSetDeviceClass(hProfile, icSigOutputClass);
|
||||
cmsSetColorSpace(hProfile, icSigGrayData);
|
||||
cmsSetPCS(hProfile, icSigLabData);
|
||||
|
||||
|
||||
// An empty LUTs is all we need
|
||||
Lut = cmsAllocLUT();
|
||||
if (Lut == NULL) {
|
||||
cmsCloseProfile(hProfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Lut -> InputChan = 3;
|
||||
Lut -> OutputChan = 1;
|
||||
|
||||
EmptyTab = cmsAllocGamma(2);
|
||||
EmptyTab ->GammaTable[0] = 0;
|
||||
EmptyTab ->GammaTable[1] = 0;
|
||||
|
||||
cmsAllocLinearTable(Lut, &EmptyTab, 2);
|
||||
|
||||
cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
|
||||
|
||||
cmsFreeLUT(Lut);
|
||||
cmsFreeGamma(EmptyTab);
|
||||
|
||||
return hProfile;
|
||||
}
|
|
@ -0,0 +1,695 @@
|
|||
//
|
||||
// Little cms
|
||||
// Copyright (C) 1998-2007 Marti Maria
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "lcms.h"
|
||||
|
||||
|
||||
// Conversions
|
||||
|
||||
void LCMSEXPORT cmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* Source)
|
||||
{
|
||||
double ISum;
|
||||
|
||||
ISum = 1./(Source -> X + Source -> Y + Source -> Z);
|
||||
|
||||
Dest -> x = (Source -> X) * ISum;
|
||||
Dest -> y = (Source -> Y) * ISum;
|
||||
Dest -> Y = Source -> Y;
|
||||
}
|
||||
|
||||
|
||||
void LCMSEXPORT cmsxyY2XYZ(LPcmsCIEXYZ Dest, const cmsCIExyY* Source)
|
||||
{
|
||||
|
||||
Dest -> X = (Source -> x / Source -> y) * Source -> Y;
|
||||
Dest -> Y = Source -> Y;
|
||||
Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y;
|
||||
}
|
||||
|
||||
|
||||
// Obtains WhitePoint from Temperature
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
|
||||
{
|
||||
double x, y;
|
||||
double T, T2, T3;
|
||||
// double M1, M2;
|
||||
|
||||
|
||||
// No optimization provided.
|
||||
|
||||
T = TempK;
|
||||
T2 = T*T; // Square
|
||||
T3 = T2*T; // Cube
|
||||
|
||||
// For correlated color temperature (T) between 4000K and 7000K:
|
||||
|
||||
if (T >= 4000. && T <= 7000.)
|
||||
{
|
||||
x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
|
||||
}
|
||||
else
|
||||
// or for correlated color temperature (T) between 7000K and 25000K:
|
||||
|
||||
if (T > 7000.0 && T <= 25000.0)
|
||||
{
|
||||
x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
|
||||
}
|
||||
else {
|
||||
cmsSignalError(LCMS_ERRC_ABORTED, "cmsWhitePointFromTemp: invalid temp");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Obtain y(x)
|
||||
|
||||
y = -3.000*(x*x) + 2.870*x - 0.275;
|
||||
|
||||
// wave factors (not used, but here for futures extensions)
|
||||
|
||||
// M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
|
||||
// M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
|
||||
|
||||
|
||||
|
||||
// Fill WhitePoint struct
|
||||
|
||||
WhitePoint -> x = x;
|
||||
WhitePoint -> y = y;
|
||||
WhitePoint -> Y = 1.0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
|
||||
// This is just an approximation, I am not handling all the non-linear
|
||||
// aspects of the RGB to XYZ process, and assumming that the gamma correction
|
||||
// has transitive property in the tranformation chain.
|
||||
//
|
||||
// the alghoritm:
|
||||
//
|
||||
// - First I build the absolute conversion matrix using
|
||||
// primaries in XYZ. This matrix is next inverted
|
||||
// - Then I eval the source white point across this matrix
|
||||
// obtaining the coeficients of the transformation
|
||||
// - Then, I apply these coeficients to the original matrix
|
||||
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, LPcmsCIExyY WhitePt,
|
||||
LPcmsCIExyYTRIPLE Primrs)
|
||||
{
|
||||
VEC3 WhitePoint, Coef;
|
||||
MAT3 Result, Primaries;
|
||||
double xn, yn;
|
||||
double xr, yr;
|
||||
double xg, yg;
|
||||
double xb, yb;
|
||||
|
||||
|
||||
xn = WhitePt -> x;
|
||||
yn = WhitePt -> y;
|
||||
xr = Primrs -> Red.x;
|
||||
yr = Primrs -> Red.y;
|
||||
xg = Primrs -> Green.x;
|
||||
yg = Primrs -> Green.y;
|
||||
xb = Primrs -> Blue.x;
|
||||
yb = Primrs -> Blue.y;
|
||||
|
||||
|
||||
// Build Primaries matrix
|
||||
VEC3init(&Primaries.v[0], xr, xg, xb);
|
||||
VEC3init(&Primaries.v[1], yr, yg, yb);
|
||||
VEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb));
|
||||
|
||||
|
||||
// Result = Primaries ^ (-1) inverse matrix
|
||||
if (!MAT3inverse(&Primaries, &Result))
|
||||
return FALSE;
|
||||
|
||||
|
||||
VEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
|
||||
|
||||
// Across inverse primaries ...
|
||||
MAT3eval(&Coef, &Result, &WhitePoint);
|
||||
|
||||
// Give us the Coefs, then I build transformation matrix
|
||||
VEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb);
|
||||
VEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb);
|
||||
VEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb));
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Compute chromatic adaptation matrix using Chad as cone matrix
|
||||
|
||||
static
|
||||
void ComputeChromaticAdaptation(LPMAT3 Conversion,
|
||||
LPcmsCIEXYZ SourceWhitePoint,
|
||||
LPcmsCIEXYZ DestWhitePoint,
|
||||
LPMAT3 Chad)
|
||||
|
||||
{
|
||||
|
||||
MAT3 Chad_Inv;
|
||||
VEC3 ConeSourceXYZ, ConeSourceRGB;
|
||||
VEC3 ConeDestXYZ, ConeDestRGB;
|
||||
MAT3 Cone, Tmp;
|
||||
|
||||
|
||||
Tmp = *Chad;
|
||||
MAT3inverse(&Tmp, &Chad_Inv);
|
||||
|
||||
VEC3init(&ConeSourceXYZ, SourceWhitePoint -> X,
|
||||
SourceWhitePoint -> Y,
|
||||
SourceWhitePoint -> Z);
|
||||
|
||||
VEC3init(&ConeDestXYZ, DestWhitePoint -> X,
|
||||
DestWhitePoint -> Y,
|
||||
DestWhitePoint -> Z);
|
||||
|
||||
MAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ);
|
||||
MAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ);
|
||||
|
||||
// Build matrix
|
||||
|
||||
VEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0);
|
||||
VEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0);
|
||||
VEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]);
|
||||
|
||||
|
||||
// Normalize
|
||||
MAT3per(&Tmp, &Cone, Chad);
|
||||
MAT3per(Conversion, &Chad_Inv, &Tmp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
|
||||
// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed
|
||||
|
||||
LCMSBOOL cmsAdaptationMatrix(LPMAT3 r, LPMAT3 ConeMatrix, LPcmsCIEXYZ FromIll, LPcmsCIEXYZ ToIll)
|
||||
{
|
||||
MAT3 LamRigg = {{ // Bradford matrix
|
||||
{{ 0.8951, 0.2664, -0.1614 }},
|
||||
{{ -0.7502, 1.7135, 0.0367 }},
|
||||
{{ 0.0389, -0.0685, 1.0296 }}
|
||||
}};
|
||||
|
||||
|
||||
if (ConeMatrix == NULL)
|
||||
ConeMatrix = &LamRigg;
|
||||
|
||||
ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
// Same as anterior, but assuming D50 destination. White point is given in xyY
|
||||
|
||||
LCMSBOOL cmsAdaptMatrixToD50(LPMAT3 r, LPcmsCIExyY SourceWhitePt)
|
||||
{
|
||||
cmsCIEXYZ Dn;
|
||||
MAT3 Bradford;
|
||||
MAT3 Tmp;
|
||||
|
||||
cmsxyY2XYZ(&Dn, SourceWhitePt);
|
||||
|
||||
cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ());
|
||||
|
||||
Tmp = *r;
|
||||
MAT3per(r, &Bradford, &Tmp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Same as anterior, but assuming D50 source. White point is given in xyY
|
||||
|
||||
LCMSBOOL cmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt)
|
||||
{
|
||||
cmsCIEXYZ Dn;
|
||||
MAT3 Bradford;
|
||||
MAT3 Tmp;
|
||||
|
||||
cmsxyY2XYZ(&Dn, DestWhitePt);
|
||||
|
||||
cmsAdaptationMatrix(&Bradford, NULL, cmsD50_XYZ(), &Dn);
|
||||
|
||||
Tmp = *r;
|
||||
MAT3per(r, &Bradford, &Tmp);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Adapts a color to a given illuminant. Original color is expected to have
|
||||
// a SourceWhitePt white point.
|
||||
|
||||
LCMSBOOL LCMSEXPORT cmsAdaptToIlluminant(LPcmsCIEXYZ Result,
|
||||
LPcmsCIEXYZ SourceWhitePt,
|
||||
LPcmsCIEXYZ Illuminant,
|
||||
LPcmsCIEXYZ Value)
|
||||
{
|
||||
MAT3 Bradford;
|
||||
VEC3 In, Out;
|
||||
|
||||
// BradfordLamRiggChromaticAdaptation(&Bradford, SourceWhitePt, Illuminant);
|
||||
|
||||
cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant);
|
||||
|
||||
VEC3init(&In, Value -> X, Value -> Y, Value -> Z);
|
||||
MAT3eval(&Out, &Bradford, &In);
|
||||
|
||||
Result -> X = Out.n[0];
|
||||
Result -> Y = Out.n[1];
|
||||
Result -> Z = Out.n[2];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
double mirek; // temp (in microreciprocal kelvin)
|
||||
double ut; // u coord of intersection w/ blackbody locus
|
||||
double vt; // v coord of intersection w/ blackbody locus
|
||||
double tt; // slope of ISOTEMPERATURE. line
|
||||
|
||||
} ISOTEMPERATURE,FAR* LPISOTEMPERATURE;
|
||||
|
||||
static ISOTEMPERATURE isotempdata[] = {
|
||||
// {Mirek, Ut, Vt, Tt }
|
||||
{0, 0.18006, 0.26352, -0.24341},
|
||||
{10, 0.18066, 0.26589, -0.25479},
|
||||
{20, 0.18133, 0.26846, -0.26876},
|
||||
{30, 0.18208, 0.27119, -0.28539},
|
||||
{40, 0.18293, 0.27407, -0.30470},
|
||||
{50, 0.18388, 0.27709, -0.32675},
|
||||
{60, 0.18494, 0.28021, -0.35156},
|
||||
{70, 0.18611, 0.28342, -0.37915},
|
||||
{80, 0.18740, 0.28668, -0.40955},
|
||||
{90, 0.18880, 0.28997, -0.44278},
|
||||
{100, 0.19032, 0.29326, -0.47888},
|
||||
{125, 0.19462, 0.30141, -0.58204},
|
||||
{150, 0.19962, 0.30921, -0.70471},
|
||||
{175, 0.20525, 0.31647, -0.84901},
|
||||
{200, 0.21142, 0.32312, -1.0182 },
|
||||
{225, 0.21807, 0.32909, -1.2168 },
|
||||
{250, 0.22511, 0.33439, -1.4512 },
|
||||
{275, 0.23247, 0.33904, -1.7298 },
|
||||
{300, 0.24010, 0.34308, -2.0637 },
|
||||
{325, 0.24702, 0.34655, -2.4681 },
|
||||
{350, 0.25591, 0.34951, -2.9641 },
|
||||
{375, 0.26400, 0.35200, -3.5814 },
|
||||
{400, 0.27218, 0.35407, -4.3633 },
|
||||
{425, 0.28039, 0.35577, -5.3762 },
|
||||
{450, 0.28863, 0.35714, -6.7262 },
|
||||
{475, 0.29685, 0.35823, -8.5955 },
|
||||
{500, 0.30505, 0.35907, -11.324 },
|
||||
{525, 0.31320, 0.35968, -15.628 },
|
||||
{550, 0.32129, 0.36011, -23.325 },
|
||||
{575, 0.32931, 0.36038, -40.770 },
|
||||
{600, 0.33724, 0.36051, -116.45 }
|
||||
};
|
||||
|
||||
#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE)
|
||||
|
||||
|
||||
// Robertson's method
|
||||
|
||||
static
|
||||
double Robertson(LPcmsCIExyY v)
|
||||
{
|
||||
int j;
|
||||
double us,vs;
|
||||
double uj,vj,tj,di,dj,mi,mj;
|
||||
double Tc = -1, xs, ys;
|
||||
|
||||
di = mi = 0;
|
||||
xs = v -> x;
|
||||
ys = v -> y;
|
||||
|
||||
// convert (x,y) to CIE 1960 (u,v)
|
||||
|
||||
us = (2*xs) / (-xs + 6*ys + 1.5);
|
||||
vs = (3*ys) / (-xs + 6*ys + 1.5);
|
||||
|
||||
|
||||
for (j=0; j < NISO; j++) {
|
||||
|
||||
uj = isotempdata[j].ut;
|
||||
vj = isotempdata[j].vt;
|
||||
tj = isotempdata[j].tt;
|
||||
mj = isotempdata[j].mirek;
|
||||
|
||||
dj = ((vs - vj) - tj * (us - uj)) / sqrt(1 + tj*tj);
|
||||
|
||||
if ((j!=0) && (di/dj < 0.0)) {
|
||||
Tc = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
|
||||
break;
|
||||
}
|
||||
|
||||
di = dj;
|
||||
mi = mj;
|
||||
}
|
||||
|
||||
|
||||
if (j == NISO) return -1;
|
||||
return Tc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
LCMSBOOL InRange(LPcmsCIExyY a, LPcmsCIExyY b, double tolerance)
|
||||
{
|
||||
double dist_x, dist_y;
|
||||
|
||||
dist_x = fabs(a->x - b->x);
|
||||
dist_y = fabs(a->y - b->y);
|
||||
|
||||
return (tolerance >= dist_x * dist_x + dist_y * dist_y);
|
||||
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
char Name[30];
|
||||
cmsCIExyY Val;
|
||||
|
||||
} WHITEPOINTS,FAR *LPWHITEPOINTS;
|
||||
|
||||
static
|
||||
int FromD40toD150(LPWHITEPOINTS pts)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = 0;
|
||||
for (i=40; i < 150; i ++)
|
||||
{
|
||||
sprintf(pts[n].Name, "D%d", i);
|
||||
cmsWhitePointFromTemp((int) (i*100.0), &pts[n].Val);
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
// To be removed in future versions
|
||||
void _cmsIdentifyWhitePoint(char *Buffer, LPcmsCIEXYZ WhitePt)
|
||||
{
|
||||
int i, n;
|
||||
cmsCIExyY Val;
|
||||
double T;
|
||||
WHITEPOINTS SomeIlluminants[140] = {
|
||||
|
||||
{"CIE illuminant A", {0.4476, 0.4074, 1.0}},
|
||||
{"CIE illuminant C", {0.3101, 0.3162, 1.0}},
|
||||
{"D65 (daylight)", {0.3127, 0.3291, 1.0}},
|
||||
};
|
||||
|
||||
n = FromD40toD150(&SomeIlluminants[3]) + 3;
|
||||
|
||||
cmsXYZ2xyY(&Val, WhitePt);
|
||||
|
||||
Val.Y = 1.;
|
||||
for (i=0; i < n; i++)
|
||||
{
|
||||
|
||||
if (InRange(&Val, &SomeIlluminants[i].Val, 0.000005))
|
||||
{
|
||||
strcpy(Buffer, "WhitePoint : ");
|
||||
strcat(Buffer, SomeIlluminants[i].Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
T = Robertson(&Val);
|
||||
|
||||
if (T > 0)
|
||||
sprintf(Buffer, "White point near %dK", (int) T);
|
||||
else
|
||||
{
|
||||
sprintf(Buffer, "Unknown white point (X:%1.2g, Y:%1.2g, Z:%1.2g)",
|
||||
WhitePt -> X, WhitePt -> Y, WhitePt -> Z);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Use darker colorant to obtain black point
|
||||
|
||||
static
|
||||
int BlackPointAsDarkerColorant(cmsHPROFILE hInput,
|
||||
int Intent,
|
||||
LPcmsCIEXYZ BlackPoint,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
WORD *Black, *White;
|
||||
cmsHTRANSFORM xform;
|
||||
icColorSpaceSignature Space;
|
||||
int nChannels;
|
||||
DWORD dwFormat;
|
||||
cmsHPROFILE hLab;
|
||||
cmsCIELab Lab;
|
||||
cmsCIEXYZ BlackXYZ, MediaWhite;
|
||||
|
||||
// If the profile does not support input direction, assume Black point 0
|
||||
if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Try to get black by using black colorant
|
||||
Space = cmsGetColorSpace(hInput);
|
||||
|
||||
if (!_cmsEndPointsBySpace(Space, &White, &Black, &nChannels)) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dwFormat = CHANNELS_SH(nChannels)|BYTES_SH(2);
|
||||
|
||||
hLab = cmsCreateLabProfile(NULL);
|
||||
|
||||
xform = cmsCreateTransform(hInput, dwFormat,
|
||||
hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC);
|
||||
|
||||
|
||||
cmsDoTransform(xform, Black, &Lab, 1);
|
||||
|
||||
// Force it to be neutral, clip to max. L* of 50
|
||||
|
||||
Lab.a = Lab.b = 0;
|
||||
if (Lab.L > 50) Lab.L = 50;
|
||||
|
||||
cmsCloseProfile(hLab);
|
||||
cmsDeleteTransform(xform);
|
||||
|
||||
cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
|
||||
|
||||
if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) {
|
||||
|
||||
*BlackPoint = BlackXYZ;
|
||||
}
|
||||
else {
|
||||
|
||||
if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) {
|
||||
|
||||
cmsTakeMediaWhitePoint(&MediaWhite, hInput);
|
||||
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ);
|
||||
}
|
||||
else
|
||||
*BlackPoint = BlackXYZ;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Get a black point of output CMYK profile, discounting any ink-limiting embedded
|
||||
// in the profile. For doing that, use perceptual intent in input direction:
|
||||
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
|
||||
|
||||
static
|
||||
int BlackPointUsingPerceptualBlack(LPcmsCIEXYZ BlackPoint,
|
||||
cmsHPROFILE hProfile,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
cmsHTRANSFORM hPercLab2CMYK, hRelColCMYK2Lab;
|
||||
cmsHPROFILE hLab;
|
||||
cmsCIELab LabIn, LabOut;
|
||||
WORD CMYK[MAXCHANNELS];
|
||||
cmsCIEXYZ BlackXYZ, MediaWhite;
|
||||
|
||||
|
||||
if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
|
||||
|
||||
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hLab = cmsCreateLabProfile(NULL);
|
||||
|
||||
hPercLab2CMYK = cmsCreateTransform(hLab, TYPE_Lab_DBL,
|
||||
hProfile, TYPE_CMYK_16,
|
||||
INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC);
|
||||
|
||||
hRelColCMYK2Lab = cmsCreateTransform(hProfile, TYPE_CMYK_16,
|
||||
hLab, TYPE_Lab_DBL,
|
||||
INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC);
|
||||
|
||||
LabIn.L = LabIn.a = LabIn.b = 0;
|
||||
|
||||
cmsDoTransform(hPercLab2CMYK, &LabIn, CMYK, 1);
|
||||
cmsDoTransform(hRelColCMYK2Lab, CMYK, &LabOut, 1);
|
||||
|
||||
if (LabOut.L > 50) LabOut.L = 50;
|
||||
LabOut.a = LabOut.b = 0;
|
||||
|
||||
cmsDeleteTransform(hPercLab2CMYK);
|
||||
cmsDeleteTransform(hRelColCMYK2Lab);
|
||||
cmsCloseProfile(hLab);
|
||||
|
||||
cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
|
||||
|
||||
if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)){
|
||||
cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
|
||||
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ);
|
||||
}
|
||||
else
|
||||
*BlackPoint = BlackXYZ;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Get Perceptual black of v4 profiles.
|
||||
static
|
||||
int GetV4PerceptualBlack(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, DWORD dwFlags)
|
||||
{
|
||||
if (dwFlags & LCMS_BPFLAGS_D50_ADAPTED) {
|
||||
|
||||
BlackPoint->X = PERCEPTUAL_BLACK_X;
|
||||
BlackPoint->Y = PERCEPTUAL_BLACK_Y;
|
||||
BlackPoint->Z = PERCEPTUAL_BLACK_Z;
|
||||
}
|
||||
else {
|
||||
|
||||
cmsCIEXYZ D50BlackPoint, MediaWhite;
|
||||
|
||||
cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
|
||||
D50BlackPoint.X = PERCEPTUAL_BLACK_X;
|
||||
D50BlackPoint.Y = PERCEPTUAL_BLACK_Y;
|
||||
D50BlackPoint.Z = PERCEPTUAL_BLACK_Z;
|
||||
|
||||
// Obtain the absolute XYZ. Adapt perceptual black back from D50 to whatever media white
|
||||
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &D50BlackPoint);
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// This function shouldn't exist at all -- there is such quantity of broken
|
||||
// profiles on black point tag, that we must somehow fix chromaticity to
|
||||
// avoid huge tint when doing Black point compensation. This function does
|
||||
// just that. There is a special flag for using black point tag, but turned
|
||||
// off by default because it is bogus on most profiles. The detection algorithm
|
||||
// involves to turn BP to neutral and to use only L component.
|
||||
|
||||
int cmsDetectBlackPoint(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, int Intent, DWORD dwFlags)
|
||||
{
|
||||
|
||||
// v4 + perceptual & saturation intents does have its own black point, and it is
|
||||
// well specified enough to use it.
|
||||
|
||||
if ((cmsGetProfileICCversion(hProfile) >= 0x4000000) &&
|
||||
(Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
|
||||
|
||||
// Matrix shaper share MRC & perceptual intents
|
||||
if (_cmsIsMatrixShaper(hProfile))
|
||||
return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, cmsFLAGS_NOTPRECALC);
|
||||
|
||||
// CLUT based - Get perceptual black point (fixed value)
|
||||
return GetV4PerceptualBlack(BlackPoint, hProfile, dwFlags);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HONOR_BLACK_POINT_TAG
|
||||
|
||||
// v2, v4 rel/abs colorimetric
|
||||
if (cmsIsTag(hProfile, icSigMediaBlackPointTag) &&
|
||||
Intent == INTENT_RELATIVE_COLORIMETRIC) {
|
||||
|
||||
cmsCIEXYZ BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
|
||||
cmsCIELab Lab;
|
||||
|
||||
// If black point is specified, then use it,
|
||||
|
||||
cmsTakeMediaBlackPoint(&BlackXYZ, hProfile);
|
||||
cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
|
||||
|
||||
// Black point is absolute XYZ, so adapt to D50 to get PCS value
|
||||
cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ);
|
||||
|
||||
// Force a=b=0 to get rid of any chroma
|
||||
|
||||
cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint);
|
||||
Lab.a = Lab.b = 0;
|
||||
if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50
|
||||
|
||||
cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab);
|
||||
|
||||
// Return BP as D50 relative or absolute XYZ (depends on flags)
|
||||
if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED))
|
||||
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &TrustedBlackPoint);
|
||||
else
|
||||
*BlackPoint = TrustedBlackPoint;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// That is about v2 profiles.
|
||||
|
||||
// If output profile, discount ink-limiting and that's all
|
||||
if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
|
||||
(cmsGetDeviceClass(hProfile) == icSigOutputClass) &&
|
||||
(cmsGetColorSpace(hProfile) == icSigCmykData))
|
||||
return BlackPointUsingPerceptualBlack(BlackPoint, hProfile, dwFlags);
|
||||
|
||||
// Nope, compute BP using current intent.
|
||||
return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
|
||||
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,185 @@
|
|||
LIBRARY
|
||||
|
||||
EXPORTS
|
||||
_cmsICCcolorSpace = _cmsICCcolorSpace
|
||||
_cmsSaveProfile = _cmsSaveProfile
|
||||
_cmsSaveProfileToMem = _cmsSaveProfileToMem
|
||||
cmsAdaptToIlluminant = cmsAdaptToIlluminant
|
||||
cmsAllocGamma = cmsAllocGamma
|
||||
cmsBuildGamma = cmsBuildGamma
|
||||
cmsSmoothGamma = cmsSmoothGamma
|
||||
cmsBuildRGB2XYZtransferMatrix= cmsBuildRGB2XYZtransferMatrix
|
||||
cmsCloseProfile = cmsCloseProfile
|
||||
cmsCreateProofingTransform = cmsCreateProofingTransform
|
||||
cmsCreateRGBProfile = cmsCreateRGBProfile
|
||||
cmsCreateTransform = cmsCreateTransform
|
||||
cmsDeleteTransform = cmsDeleteTransform
|
||||
cmsDoTransform = cmsDoTransform
|
||||
cmsErrorAction = cmsErrorAction
|
||||
cmsFreeGamma = cmsFreeGamma
|
||||
cmsGetAlarmCodes = cmsGetAlarmCodes
|
||||
cmsGetColorSpace = cmsGetColorSpace
|
||||
cmsGetDeviceClass = cmsGetDeviceClass
|
||||
cmsGetPCS = cmsGetPCS
|
||||
cmsIsIntentSupported = cmsIsIntentSupported
|
||||
cmsIsTag = cmsIsTag
|
||||
cmsJoinGamma = cmsJoinGamma
|
||||
cmsJoinGammaEx = cmsJoinGammaEx
|
||||
cmsOpenProfileFromFile = cmsOpenProfileFromFile
|
||||
cmsOpenProfileFromMem = cmsOpenProfileFromMem
|
||||
cmsReverseGamma = cmsReverseGamma
|
||||
cmsSetAlarmCodes = cmsSetAlarmCodes
|
||||
cmsTakeColorants = cmsTakeColorants
|
||||
cmsTakeIluminant = cmsTakeIluminant
|
||||
cmsTakeMediaBlackPoint = cmsTakeMediaBlackPoint
|
||||
cmsTakeMediaWhitePoint = cmsTakeMediaWhitePoint
|
||||
cmsTakeProductDesc = cmsTakeProductDesc
|
||||
cmsTakeProductInfo = cmsTakeProductInfo
|
||||
cmsTakeProductName = cmsTakeProductName
|
||||
cmsTakeRenderingIntent = cmsTakeRenderingIntent
|
||||
cmsWhitePointFromTemp = cmsWhitePointFromTemp
|
||||
cmsXYZ2xyY = cmsXYZ2xyY
|
||||
cmsxyY2XYZ = cmsxyY2XYZ
|
||||
cmsLCh2Lab = cmsLCh2Lab
|
||||
cmsLab2LCh = cmsLab2LCh
|
||||
cmsLab2XYZ = cmsLab2XYZ
|
||||
cmsXYZ2Lab = cmsXYZ2Lab
|
||||
cmsClampLab = cmsClampLab
|
||||
cmsCreateLabProfile = cmsCreateLabProfile
|
||||
cmsCreateXYZProfile = cmsCreateXYZProfile
|
||||
cmsCreate_sRGBProfile = cmsCreate_sRGBProfile
|
||||
cmsD50_XYZ = cmsD50_XYZ
|
||||
cmsD50_xyY = cmsD50_xyY
|
||||
cmsDeltaE = cmsDeltaE
|
||||
cmsCIE94DeltaE = cmsCIE94DeltaE
|
||||
cmsBFDdeltaE = cmsBFDdeltaE
|
||||
cmsCMCdeltaE = cmsCMCdeltaE
|
||||
cmsCIE2000DeltaE = cmsCIE2000DeltaE
|
||||
cmsFloat2LabEncoded = cmsFloat2LabEncoded
|
||||
cmsFloat2XYZEncoded = cmsFloat2XYZEncoded
|
||||
cmsLabEncoded2Float = cmsLabEncoded2Float
|
||||
cmsXYZEncoded2Float = cmsXYZEncoded2Float
|
||||
cmsBuildParametricGamma = cmsBuildParametricGamma
|
||||
cmsCIECAM97sInit = cmsCIECAM97sInit
|
||||
cmsCIECAM97sDone = cmsCIECAM97sDone
|
||||
cmsCIECAM97sForward = cmsCIECAM97sForward
|
||||
cmsCIECAM97sReverse = cmsCIECAM97sReverse
|
||||
cmsCIECAM02Init = cmsCIECAM02Init
|
||||
cmsCIECAM02Done = cmsCIECAM02Done
|
||||
cmsCIECAM02Forward = cmsCIECAM02Forward
|
||||
cmsCIECAM02Reverse = cmsCIECAM02Reverse
|
||||
cmsCreateMultiprofileTransform = cmsCreateMultiprofileTransform
|
||||
cmsAddTag = cmsAddTag
|
||||
cmsAllocLUT = cmsAllocLUT
|
||||
cmsAllocLinearTable = cmsAllocLinearTable
|
||||
cmsAlloc3DGrid = cmsAlloc3DGrid
|
||||
cmsFreeLUT = cmsFreeLUT
|
||||
cmsEvalLUT = cmsEvalLUT
|
||||
cmsReadICCLut = cmsReadICCLut
|
||||
cmsSample3DGrid = cmsSample3DGrid
|
||||
cmsSetMatrixLUT = cmsSetMatrixLUT
|
||||
cmsSetMatrixLUT4 = cmsSetMatrixLUT4
|
||||
cmsDupGamma = cmsDupGamma
|
||||
cmsReadICCGamma = cmsReadICCGamma
|
||||
cmsReadICCGammaReversed = cmsReadICCGammaReversed
|
||||
cmsSetErrorHandler = cmsSetErrorHandler
|
||||
cmsChangeBuffersFormat = cmsChangeBuffersFormat
|
||||
cmsCreateGrayProfile = cmsCreateGrayProfile
|
||||
cmsCreateInkLimitingDeviceLink = cmsCreateInkLimitingDeviceLink
|
||||
cmsCreateLinearizationDeviceLink = cmsCreateLinearizationDeviceLink
|
||||
cmsEstimateGamma = cmsEstimateGamma
|
||||
cmsEstimateGammaEx = cmsEstimateGammaEx
|
||||
cmsNamedColorCount = cmsNamedColorCount
|
||||
cmsNamedColorInfo = cmsNamedColorInfo
|
||||
cmsNamedColorIndex = cmsNamedColorIndex
|
||||
cmsSetColorSpace = cmsSetColorSpace
|
||||
cmsSetDeviceClass = cmsSetDeviceClass
|
||||
cmsSetLanguage = cmsSetLanguage
|
||||
cmsSetPCS = cmsSetPCS
|
||||
cmsTakeCharTargetData = cmsTakeCharTargetData
|
||||
cmsTransform2DeviceLink = cmsTransform2DeviceLink
|
||||
_cmsChannelsOf = _cmsChannelsOf
|
||||
cmsFreeGammaTriple = cmsFreeGammaTriple
|
||||
cmsSetRenderingIntent = cmsSetRenderingIntent
|
||||
cmsDupLUT = cmsDupLUT
|
||||
cmsGetUserFormatters = cmsGetUserFormatters
|
||||
cmsSetUserFormatters = cmsSetUserFormatters
|
||||
cmsCreateBCHSWabstractProfile = cmsCreateBCHSWabstractProfile
|
||||
cmsGetPostScriptCSA = cmsGetPostScriptCSA
|
||||
cmsGetPostScriptCRD = cmsGetPostScriptCRD
|
||||
cmsGetPostScriptCRDEx = cmsGetPostScriptCRDEx
|
||||
cmsReadProfileSequenceDescription = cmsReadProfileSequenceDescription
|
||||
cmsTakeManufacturer = cmsTakeManufacturer
|
||||
cmsTakeModel = cmsTakeModel
|
||||
cmsSetProfileID = cmsSetProfileID
|
||||
cmsTakeProfileID = cmsTakeProfileID
|
||||
cmsSetHeaderFlags = cmsSetHeaderFlags
|
||||
cmsTakeHeaderFlags = cmsTakeHeaderFlags
|
||||
cmsTakeCopyright = cmsTakeCopyright
|
||||
_cmsSetLUTdepth = _cmsSetLUTdepth
|
||||
_cmsAddXYZTag = _cmsAddXYZTag
|
||||
_cmsAddLUTTag = _cmsAddLUTTag
|
||||
_cmsAddTextTag = _cmsAddTextTag
|
||||
_cmsAddGammaTag = _cmsAddGammaTag
|
||||
_cmsAddChromaticityTag = _cmsAddChromaticityTag
|
||||
_cmsAddSequenceDescriptionTag = _cmsAddSequenceDescriptionTag
|
||||
_cmsAddNamedColorTag = _cmsAddNamedColorTag
|
||||
_cmsLCMScolorSpace = _cmsLCMScolorSpace
|
||||
cmsFloat2LabEncoded4 = cmsFloat2LabEncoded4
|
||||
cmsLabEncoded2Float4 = cmsLabEncoded2Float4
|
||||
cmsGetProfileICCversion = cmsGetProfileICCversion
|
||||
cmsIT8Alloc = cmsIT8Alloc
|
||||
cmsIT8Free = cmsIT8Free
|
||||
cmsIT8TableCount = cmsIT8TableCount
|
||||
cmsIT8SetTable = cmsIT8SetTable
|
||||
cmsIT8LoadFromFile = cmsIT8LoadFromFile
|
||||
cmsIT8LoadFromMem = cmsIT8LoadFromMem
|
||||
cmsIT8SaveToFile = cmsIT8SaveToFile
|
||||
cmsIT8GetSheetType = cmsIT8GetSheetType
|
||||
cmsIT8SetSheetType = cmsIT8SetSheetType
|
||||
cmsIT8SetComment = cmsIT8SetComment
|
||||
cmsIT8SetPropertyStr = cmsIT8SetPropertyStr
|
||||
cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl
|
||||
cmsIT8SetPropertyHex = cmsIT8SetPropertyHex
|
||||
cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked
|
||||
cmsIT8GetProperty = cmsIT8GetProperty
|
||||
cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl
|
||||
cmsIT8EnumProperties = cmsIT8EnumProperties
|
||||
cmsIT8GetDataRowCol = cmsIT8GetDataRowCol
|
||||
cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl
|
||||
cmsIT8SetDataRowCol = cmsIT8SetDataRowCol
|
||||
cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl
|
||||
cmsIT8GetData = cmsIT8GetData
|
||||
cmsIT8GetDataDbl = cmsIT8GetDataDbl
|
||||
cmsIT8SetData = cmsIT8SetData
|
||||
cmsIT8SetDataDbl = cmsIT8SetDataDbl
|
||||
cmsIT8SetDataFormat = cmsIT8SetDataFormat
|
||||
cmsIT8EnumDataFormat = cmsIT8EnumDataFormat
|
||||
cmsIT8GetPatchName = cmsIT8GetPatchName
|
||||
cmsIT8SetTableByLabel = cmsIT8SetTableByLabel
|
||||
cmsReadICCText = cmsReadICCText
|
||||
cmsReadICCTextEx = cmsReadICCTextEx
|
||||
cmsCreateLab4Profile = cmsCreateLab4Profile
|
||||
cmsCreateNULLProfile = cmsCreateNULLProfile
|
||||
cmsIT8DefineDblFormat = cmsIT8DefineDblFormat
|
||||
cmsIT8GetDataFormat = cmsIT8GetDataFormat
|
||||
cmsSetProfileICCversion = cmsSetProfileICCversion
|
||||
cmsTakeCalibrationDateTime = cmsTakeCalibrationDateTime
|
||||
cmsTakeCreationDateTime = cmsTakeCreationDateTime
|
||||
_cmsIsMatrixShaper = _cmsIsMatrixShaper
|
||||
_cmsAddColorantTableTag = _cmsAddColorantTableTag
|
||||
_cmsAddDateTimeTag = _cmsAddDateTimeTag
|
||||
cmsEvalLUTreverse = cmsEvalLUTreverse
|
||||
cmsGetTagCount = cmsGetTagCount
|
||||
cmsGetTagSignature = cmsGetTagSignature
|
||||
cmsIT8SaveToMem = cmsIT8SaveToMem
|
||||
cmsReadColorantTable = cmsReadColorantTable
|
||||
cmsSetAdaptationState = cmsSetAdaptationState
|
||||
cmsSetHeaderAttributes = cmsSetHeaderAttributes
|
||||
cmsTakeHeaderAttributes = cmsTakeHeaderAttributes
|
||||
cmsSetCMYKPreservationStrategy = cmsSetCMYKPreservationStrategy
|
||||
cmsFreeProfileSequenceDescription = cmsFreeProfileSequenceDescription
|
||||
_cmsAddChromaticAdaptationTag = _cmsAddChromaticAdaptationTag
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
#
|
||||
|
|
Загрузка…
Ссылка в новой задаче