6523398: OSS CMM: Need to implement writing ICC profile tags in new lcms library

Reviewed-by: igor, prr
This commit is contained in:
Andrew Brygin 2010-09-09 16:20:55 +04:00
parent 2d2d444adf
commit 899dcb6de0
38 changed files with 25196 additions and 20386 deletions

View File

@ -25,7 +25,6 @@
FILES_c = \
cmscam02.c \
cmscam97.c \
cmscgats.c \
cmscnvrt.c \
cmserr.c \
@ -35,13 +34,17 @@ FILES_c = \
cmsio0.c \
cmsio1.c \
cmslut.c \
cmsmatsh.c \
cmsmd5.c \
cmsmtrx.c \
cmsnamed.c \
cmsopt.c \
cmspack.c \
cmspcs.c \
cmsplugin.c \
cmsps2.c \
cmssamp.c \
cmssm.c \
cmstypes.c \
cmsvirt.c \
cmswtpnt.c \
cmsxform.c \

View File

@ -25,7 +25,6 @@
FILES_c = \
cmscam02.c \
cmscam97.c \
cmscgats.c \
cmscnvrt.c \
cmserr.c \
@ -35,13 +34,17 @@ FILES_c = \
cmsio0.c \
cmsio1.c \
cmslut.c \
cmsmatsh.c \
cmsmd5.c \
cmsmtrx.c \
cmsnamed.c \
cmsopt.c \
cmspack.c \
cmspcs.c \
cmsplugin.c \
cmsps2.c \
cmssamp.c \
cmssm.c \
cmstypes.c \
cmsvirt.c \
cmswtpnt.c \
cmsxform.c \

View File

@ -80,8 +80,8 @@ vpath %.c $(SHARE_SRC)/native/$(PKGDIR)
vpath %.c $(SHARE_SRC)/native/sun/java2d
ifeq ($(PLATFORM), windows)
OTHER_LDLIBS = user32.lib version.lib $(OBJDIR)/../../../sun.awt/awt/$(OBJDIRNAME)/awt.lib
OTHER_CFLAGS += -DCMS_IS_WINDOWS_ -Dsqrtf=sqrt
OTHER_LDLIBS = $(OBJDIR)/../../../sun.awt/awt/$(OBJDIRNAME)/awt.lib
OTHER_INCLUDES += -I$(SHARE_SRC)/native/sun/java2d \
-I$(SHARE_SRC)/native/sun/awt/debug

View File

@ -100,12 +100,12 @@ public class CMSManager {
public long loadProfile(byte[] data) {
System.err.print(cName + ".loadProfile");
long profileID = tcmm.loadProfile(data);
System.err.println("(ID=" + profileID + ")");
System.err.printf("(ID=%x)\n", profileID);
return profileID;
}
public void freeProfile(long profileID) {
System.err.println(cName + ".freeProfile(ID=" + profileID + ")");
System.err.printf(cName + ".freeProfile(ID=%x)\n", profileID);
tcmm.freeProfile(profileID);
}
@ -123,8 +123,8 @@ public class CMSManager {
}
public int getTagSize(long profileID, int tagSignature) {
System.err.print(cName + ".getTagSize(ID=" + profileID +
", TagSig=" + tagSignature + ")");
System.err.printf(cName + ".getTagSize(ID=%x, TagSig=%s)",
profileID, signatureToString(tagSignature));
int size = tcmm.getTagSize(profileID, tagSignature);
System.err.println("=" + size);
return size;
@ -132,8 +132,8 @@ public class CMSManager {
public void getTagData(long profileID, int tagSignature,
byte[] data) {
System.err.print(cName + ".getTagData(ID=" + profileID +
", TagSig=" + tagSignature + ")");
System.err.printf(cName + ".getTagData(ID=%x, TagSig=%s)",
profileID, signatureToString(tagSignature));
System.err.println(" requested " + data.length + " byte(s)");
tcmm.getTagData(profileID, tagSignature, data);
}
@ -158,5 +158,13 @@ public class CMSManager {
System.err.println(cName + ".createTransform(ColorTransform[])");
return tcmm.createTransform(transforms);
}
private static String signatureToString(int sig) {
return String.format("%c%c%c%c",
(char)(0xff & (sig >> 24)),
(char)(0xff & (sig >> 16)),
(char)(0xff & (sig >> 8)),
(char)(0xff & (sig )));
}
}
}

View File

@ -53,7 +53,8 @@ public class LCMS implements PCMM {
public static native long getProfileID(ICC_Profile profile);
public static native long createNativeTransform(
long[] profileIDs, int renderType, Object disposerRef);
long[] profileIDs, int renderType, int inFormatter, int outFormatter,
Object disposerRef);
/**
* Constructs ColorTransform object corresponding to an ICC_profile

View File

@ -55,11 +55,17 @@ import sun.java2d.cmm.lcms.*;
public class LCMSTransform implements ColorTransform {
long ID;
private int inFormatter;
private int outFormatter;
ICC_Profile[] profiles;
long [] profileIDs;
int renderType;
int transformType;
private int numInComponents = -1;
private int numOutComponents = -1;
private Object disposerReferent = new Object();
/* the class initializer */
@ -80,6 +86,14 @@ public class LCMSTransform implements ColorTransform {
this.renderType = (renderType == ColorTransform.Any)?
ICC_Profile.icPerceptual : renderType;
this.transformType = transformType;
/* Note that ICC_Profile.getNumComponents() is quite expensive
* (it may results in a reading of the profile header).
* So, here we cache the number of components of input and
* output profiles for further usage.
*/
numInComponents = profiles[0].getNumComponents();
numOutComponents = profiles[profiles.length - 1].getNumComponents();
}
public LCMSTransform (ColorTransform[] transforms) {
@ -99,26 +113,51 @@ public class LCMSTransform implements ColorTransform {
j += curTrans.profiles.length;
}
renderType = ((LCMSTransform)transforms[0]).renderType;
ID = LCMS.createNativeTransform(profileIDs, renderType,
disposerReferent);
/* Note that ICC_Profile.getNumComponents() is quite expensive
* (it may results in a reading of the profile header).
* So, here we cache the number of components of input and
* output profiles for further usage.
*/
numInComponents = profiles[0].getNumComponents();
numOutComponents = profiles[profiles.length - 1].getNumComponents();
}
public int getNumInComponents() {
return profiles[0].getNumComponents();
return numInComponents;
}
public int getNumOutComponents() {
return profiles[profiles.length - 1].getNumComponents();
return numOutComponents;
}
private synchronized void doTransform(LCMSImageLayout in,
LCMSImageLayout out) {
// update native transfrom if needed
if (ID == 0L ||
inFormatter != in.pixelType ||
outFormatter != out.pixelType) {
if (ID != 0L) {
// Disposer will destroy forgotten transform
disposerReferent = new Object();
}
inFormatter = in.pixelType;
outFormatter = out.pixelType;
ID = LCMS.createNativeTransform(profileIDs, renderType,
inFormatter, outFormatter,
disposerReferent);
}
LCMS.colorConvert(this, in, out);
}
public void colorConvert(BufferedImage src, BufferedImage dst) {
if (LCMSImageLayout.isSupported(src) &&
LCMSImageLayout.isSupported(dst))
{
synchronized(this) {
LCMS.colorConvert(this, new LCMSImageLayout(src),
new LCMSImageLayout(dst));
}
doTransform(new LCMSImageLayout(src), new LCMSImageLayout(dst));
return;
}
LCMSImageLayout srcIL, dstIL;
@ -204,9 +243,8 @@ public class LCMSTransform implements ColorTransform {
}
}
// color convert srcLine to dstLine
synchronized (this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
// convert dst scanline
pixel = null;
idx = 0;
@ -263,9 +301,8 @@ public class LCMSTransform implements ColorTransform {
}
}
// color convert srcLine to dstLine
synchronized(this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
// convert dst scanline
pixel = null;
idx = 0;
@ -377,9 +414,7 @@ public class LCMSTransform implements ColorTransform {
}
// color convert srcLine to dstLine
synchronized(this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
// store dst scanline
xd = dst.getMinX();
@ -470,9 +505,7 @@ public class LCMSTransform implements ColorTransform {
}
// color convert srcLine to dstLine
synchronized(this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
// store dst scanline
xd = dst.getMinX();
@ -513,9 +546,8 @@ public class LCMSTransform implements ColorTransform {
}
// color convert srcLine to dstLine
synchronized(this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
// store dst scanline
xd = dst.getMinX();
idx = 0;
@ -550,9 +582,7 @@ public class LCMSTransform implements ColorTransform {
LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2);
synchronized(this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
return dst;
}
@ -572,9 +602,7 @@ public class LCMSTransform implements ColorTransform {
LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) |
LCMSImageLayout.BYTES_SH(1), getNumOutComponents());
synchronized(this) {
LCMS.colorConvert(this, srcIL, dstIL);
}
doTransform(srcIL, dstIL);
return dst;
}

View File

@ -24,11 +24,13 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "sun_java2d_cmm_lcms_LCMS.h"
#include "jni_util.h"
#include "Trace.h"
#include "Disposer.h"
#include "lcms.h"
#include "lcms2.h"
#define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary
@ -38,10 +40,10 @@
#else
static
void AdjustEndianess32(LPBYTE pByte)
void AdjustEndianess32(cmsUInt8Number *pByte)
{
BYTE temp1;
BYTE temp2;
cmsUInt8Number temp1;
cmsUInt8Number temp2;
temp1 = *pByte++;
temp2 = *pByte++;
@ -57,11 +59,11 @@ void AdjustEndianess32(LPBYTE pByte)
// big endian notation.
static
icInt32Number TransportValue32(icInt32Number Value)
cmsInt32Number TransportValue32(cmsInt32Number Value)
{
icInt32Number Temp = Value;
cmsInt32Number Temp = Value;
AdjustEndianess32((LPBYTE) &Temp);
AdjustEndianess32((cmsUInt8Number*) &Temp);
return Temp;
}
@ -84,7 +86,13 @@ icInt32Number TransportValue32(icInt32Number Value)
/* Default temp profile list size */
#define DF_ICC_BUF_SIZE 32
#define ERR_MSG_SIZE 20
#define ERR_MSG_SIZE 256
#ifdef _MSC_VER
# ifndef snprintf
# define snprintf _snprintf
# endif
#endif
typedef union storeID_s { /* store SProfile stuff in a Java Long */
cmsHPROFILE pf;
@ -93,6 +101,11 @@ typedef union storeID_s { /* store SProfile stuff in a Java Long */
jlong j;
} storeID_t, *storeID_p;
typedef union {
cmsTagSignature cms;
jint j;
} TagSignature_t, *TagSignature_p;
static jfieldID Trans_profileIDs_fID;
static jfieldID Trans_renderType_fID;
static jfieldID Trans_ID_fID;
@ -108,21 +121,26 @@ static jfieldID PF_ID_fID;
JavaVM *javaVM;
int errorHandler(int errorCode, const char *errorText) {
void errorHandler(cmsContext ContextID, cmsUInt32Number errorCode,
const char *errorText) {
JNIEnv *env;
char errMsg[ERR_MSG_SIZE];
/* We can safely use sprintf here because error message has limited size */
sprintf(errMsg, "LCMS error %d", errorCode);
int count = snprintf(errMsg, ERR_MSG_SIZE,
"LCMS error %d: %s", errorCode, errorText);
if (count < 0 || count >= ERR_MSG_SIZE) {
count = ERR_MSG_SIZE - 1;
}
errMsg[count] = 0;
(*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
JNU_ThrowByName(env, "java/awt/color/CMMException", errMsg);
return 1;
}
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
javaVM = jvm;
cmsSetErrorHandler(errorHandler);
cmsSetLogErrorHandler(errorHandler);
return JNI_VERSION_1_6;
}
@ -141,11 +159,10 @@ void LCMS_freeTransform(JNIEnv *env, jlong ID)
*/
JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
(JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType,
jobject disposerRef)
jint inFormatter, jint outFormatter, jobject disposerRef)
{
LPLCMSICCPROFILE _iccArray[DF_ICC_BUF_SIZE];
LPLCMSICCPROFILE *iccArray = &_iccArray[0];
cmsHTRANSFORM transform;
cmsHPROFILE _iccArray[DF_ICC_BUF_SIZE];
cmsHPROFILE *iccArray = &_iccArray[0];
storeID_t sTrans;
int i, j, size;
jlong* ids;
@ -154,17 +171,19 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
ids = (*env)->GetPrimitiveArrayCritical(env, profileIDs, 0);
if (DF_ICC_BUF_SIZE < size*2) {
iccArray = (LPLCMSICCPROFILE*) malloc(
size*2*sizeof(LPLCMSICCPROFILE));
iccArray = (cmsHPROFILE*) malloc(
size*2*sizeof(cmsHPROFILE));
if (iccArray == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL");
return NULL;
return 0L;
}
}
j = 0;
for (i = 0; i < size; i++) {
LPLCMSICCPROFILE icc;
cmsHPROFILE icc;
cmsColorSpaceSignature cs;
sTrans.j = ids[i];
icc = sTrans.pf;
iccArray[j++] = icc;
@ -172,16 +191,17 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
/* Middle non-abstract profiles should be doubled before passing to
* the cmsCreateMultiprofileTransform function
*/
cs = cmsGetColorSpace(icc);
if (size > 2 && i != 0 && i != size - 1 &&
icc->ColorSpace != icSigXYZData &&
icc->ColorSpace != icSigLabData)
cs != cmsSigXYZData && cs != cmsSigLabData)
{
iccArray[j++] = icc;
}
}
sTrans.xf = cmsCreateMultiprofileTransform(iccArray, j,
0, 0, renderType, 0);
inFormatter, outFormatter, renderType, 0);
(*env)->ReleasePrimitiveArrayCritical(env, profileIDs, ids, 0);
@ -190,12 +210,13 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
"sTrans.xf == NULL");
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Cannot get color transform");
} else {
Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j);
}
if (iccArray != &_iccArray[0]) {
free(iccArray);
}
Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j);
return sTrans.j;
}
@ -215,7 +236,8 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfile
dataArray = (*env)->GetByteArrayElements (env, data, 0);
dataSize = (*env)->GetArrayLength (env, data);
sProf.pf = cmsOpenProfileFromMem((LPVOID)dataArray, (DWORD) dataSize);
sProf.pf = cmsOpenProfileFromMem((const void *)dataArray,
(cmsUInt32Number) dataSize);
(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
@ -254,20 +276,17 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_freeProfile
JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSize
(JNIEnv *env, jobject obj, jlong id)
{
LPLCMSICCPROFILE Icc;
storeID_t sProf;
unsigned char pfSize[4];
cmsUInt32Number pfSize = 0;
sProf.j = id;
Icc = (LPLCMSICCPROFILE) sProf.pf;
Icc -> Seek(Icc, 0);
Icc -> Read(pfSize, 4, 1, Icc);
/* TODO: It's a correct but non-optimal for BE machines code, so should
* be special cased for them
*/
return (pfSize[0]<<24) | (pfSize[1]<<16) | (pfSize[2]<<8) |
pfSize[3];
if (cmsSaveProfileToMem(sProf.pf, NULL, &pfSize) && ((jint)pfSize > 0)) {
return (jint)pfSize;
} else {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Can not access specified profile.");
return -1;
}
}
/*
@ -278,29 +297,46 @@ JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSize
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileData
(JNIEnv *env, jobject obj, jlong id, jbyteArray data)
{
LPLCMSICCPROFILE Icc;
storeID_t sProf;
unsigned char pfSize[4];
jint size;
jbyte* dataArray;
cmsUInt32Number pfSize = 0;
cmsBool status;
sProf.j = id;
Icc = (LPLCMSICCPROFILE) sProf.pf;
Icc -> Seek(Icc, 0);
Icc -> Read(pfSize, 4, 1, Icc);
// determine actual profile size
if (!cmsSaveProfileToMem(sProf.pf, NULL, &pfSize)) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Can not access specified profile.");
return;
}
// verify java buffer capacity
size = (*env)->GetArrayLength(env, data);
if (0 >= size || pfSize > (cmsUInt32Number)size) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Insufficient buffer capacity.");
return;
}
dataArray = (*env)->GetByteArrayElements (env, data, 0);
Icc->Seek(Icc, 0);
/* TODO: It's a correct but non-optimal for BE machines code, so should
* be special cased for them
*/
Icc->Read(dataArray, 1,
(pfSize[0]<<24) | (pfSize[1]<<16) | (pfSize[2]<<8) | pfSize[3],
Icc);
status = cmsSaveProfileToMem(sProf.pf, dataArray, &pfSize);
(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
if (!status) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Can not access specified profile.");
return;
}
}
/* Get profile header info */
cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
/*
* Class: sun_java2d_cmm_lcms_LCMS
* Method: getTagSize
@ -309,24 +345,21 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileData
JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagSize
(JNIEnv *env, jobject obj, jlong id, jint tagSig)
{
LPLCMSICCPROFILE Icc;
storeID_t sProf;
int i;
jint result;
TagSignature_t sig;
jint result = -1;
sProf.j = id;
Icc = (LPLCMSICCPROFILE) sProf.pf;
sig.j = tagSig;
if (tagSig == SigHead) {
result = sizeof(icHeader);
result = sizeof(cmsICCHeader);
} else {
i = _cmsSearchTag(Icc, tagSig, FALSE);
if (i >= 0) {
result = Icc->TagSizes[i];
if (cmsIsTag(sProf.pf, sig.cms)) {
result = cmsReadRawTag(sProf.pf, sig.cms, NULL, 0);
} else {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"ICC profile tag not found");
result = -1;
}
}
@ -341,43 +374,83 @@ JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagSize
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagData
(JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
{
LPLCMSICCPROFILE Icc;
storeID_t sProf;
TagSignature_t sig;
cmsInt32Number tagSize;
jbyte* dataArray;
int i, tagSize;
jint bufSize;
sProf.j = id;
Icc = (LPLCMSICCPROFILE) sProf.pf;
sig.j = tagSig;
if (tagSig == SigHead) {
cmsBool status;
bufSize =(*env)->GetArrayLength(env, data);
if (bufSize < sizeof(cmsICCHeader)) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Insufficient buffer capacity");
return;
}
dataArray = (*env)->GetByteArrayElements (env, data, 0);
tagSize =(*env)->GetArrayLength(env, data);
Icc -> Seek(Icc, 0);
Icc -> Read(dataArray, sizeof(icHeader), 1, Icc);
if (dataArray == NULL) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Unable to get buffer");
return;
}
status = _getHeaderInfo(sProf.pf, dataArray, bufSize);
(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
if (!status) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"ICC Profile header not found");
}
return;
}
i = _cmsSearchTag(Icc, tagSig, FALSE);
if (i >=0) {
tagSize = Icc->TagSizes[i];
dataArray = (*env)->GetByteArrayElements (env, data, 0);
Icc->Seek(Icc, Icc->TagOffsets[i]);
Icc->Read(dataArray, 1, tagSize, Icc);
(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
if (cmsIsTag(sProf.pf, sig.cms)) {
tagSize = cmsReadRawTag(sProf.pf, sig.cms, NULL, 0);
} else {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"ICC profile tag not found");
return;
}
JNU_ThrowByName(env, "java/awt/color/CMMException",
"ICC profile tag not found");
// verify data buffer capacity
bufSize = (*env)->GetArrayLength(env, data);
if (tagSize < 0 || 0 > bufSize || tagSize > bufSize) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Insufficient buffer capacity.");
return;
}
dataArray = (*env)->GetByteArrayElements (env, data, 0);
if (dataArray == NULL) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Unable to get buffer");
return;
}
bufSize = cmsReadRawTag(sProf.pf, sig.cms, dataArray, tagSize);
(*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
if (bufSize != tagSize) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Can not get tag data.");
}
return;
}
// Modify data for a tag in a profile
LCMSBOOL LCMSEXPORT _cmsModifyTagData(cmsHPROFILE hProfile,
icTagSignature sig, void *data, size_t size);
/*
* Class: sun_java2d_cmm_lcms_LCMS
* Method: setTagData
@ -386,23 +459,32 @@ LCMSBOOL LCMSEXPORT _cmsModifyTagData(cmsHPROFILE hProfile,
JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagData
(JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
{
cmsHPROFILE profile;
storeID_t sProf;
TagSignature_t sig;
cmsBool status;
jbyte* dataArray;
int tagSize;
sProf.j = id;
sig.j = tagSig;
tagSize =(*env)->GetArrayLength(env, data);
dataArray = (*env)->GetByteArrayElements(env, data, 0);
if (tagSig == SigHead) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_setTagData on icSigHead not "
"permitted");
return;
status = _setHeaderInfo(sProf.pf, dataArray, tagSize);
} else {
status = cmsWriteRawTag(sProf.pf, sig.cms, dataArray, tagSize);
}
sProf.j = id;
profile = (cmsHPROFILE) sProf.pf;
dataArray = (*env)->GetByteArrayElements(env, data, 0);
tagSize =(*env)->GetArrayLength(env, data);
_cmsModifyTagData(profile, (icTagSignature) tagSig, dataArray, tagSize);
(*env)->ReleaseByteArrayElements(env, data, dataArray, 0);
if (!status) {
JNU_ThrowByName(env, "java/awt/color/CMMException",
"Can not write tag data.");
}
}
void* getILData (JNIEnv *env, jobject img, jint* pDataType,
@ -456,7 +538,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
(JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst)
{
storeID_t sTrans;
int size, inFmt, outFmt, srcDType, dstDType, outSize, renderType;
int inFmt, outFmt, srcDType, dstDType;
int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset;
int width, height, i;
void* inputBuffer;
@ -483,8 +565,6 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert
}
#endif
sTrans.j = (*env)->GetLongField (env, trans, Trans_ID_fID);
cmsChangeBuffersFormat(sTrans.xf, inFmt, outFmt);
if (sTrans.xf == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL");
@ -565,190 +645,54 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_initLCMS
PF_ID_fID = (*env)->GetFieldID (env, Pf, "ID", "J");
}
LCMSBOOL _cmsModifyTagData(cmsHPROFILE hProfile, icTagSignature sig,
void *data, size_t size)
cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize)
{
LCMSBOOL isNew;
int i, idx, delta, count;
LPBYTE padChars[3] = {0, 0, 0};
LPBYTE beforeBuf, afterBuf, ptr;
size_t beforeSize, afterSize;
icUInt32Number profileSize, temp;
LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
cmsUInt32Number pfSize = 0;
cmsUInt8Number* pfBuffer = NULL;
cmsBool status = FALSE;
isNew = FALSE;
idx = _cmsSearchTag(Icc, sig, FALSE);
if (idx < 0) {
isNew = TRUE;
idx = Icc->TagCount++;
if (Icc->TagCount >= MAX_TABLE_TAG) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "_cmsModifyTagData: Too many tags "
"(%d)\n", Icc->TagCount);
Icc->TagCount = MAX_TABLE_TAG-1;
return FALSE;
}
}
if (!cmsSaveProfileToMem(pf, NULL, &pfSize) ||
pfSize < sizeof(cmsICCHeader) ||
bufferSize < sizeof(cmsICCHeader))
{
return FALSE;
}
/* Read in size from header */
Icc->Seek(Icc, 0);
Icc->Read(&profileSize, sizeof(icUInt32Number), 1, Icc);
AdjustEndianess32((LPBYTE) &profileSize);
pfBuffer = malloc(pfSize);
if (pfBuffer == NULL) {
return FALSE;
}
/* Compute the change in profile size */
if (isNew) {
delta = sizeof(icTag) + ALIGNLONG(size);
} else {
delta = ALIGNLONG(size) - ALIGNLONG(Icc->TagSizes[idx]);
}
/* Add tag to internal structures */
ptr = malloc(size);
if (ptr == NULL) {
if(isNew) {
Icc->TagCount--;
}
J2dRlsTraceLn(J2D_TRACE_ERROR, "_cmsModifyTagData: ptr == NULL");
return FALSE;
}
/* We change the size of Icc here only if we know it'll actually
* grow: if Icc is about to shrink we must wait until we've read
* the previous data. */
if (delta > 0) {
if (!Icc->Grow(Icc, delta)) {
free(ptr);
if(isNew) {
Icc->TagCount--;
}
J2dRlsTraceLn(J2D_TRACE_ERROR,
"_cmsModifyTagData: Icc->Grow() == FALSE");
return FALSE;
}
}
/* Compute size of tag data before/after the modified tag */
beforeSize = ((isNew)?profileSize:Icc->TagOffsets[idx]) -
Icc->TagOffsets[0];
if (Icc->TagCount == (idx + 1)) {
afterSize = 0;
} else {
afterSize = profileSize - Icc->TagOffsets[idx+1];
}
/* Make copies of the data before/after the modified tag */
if (beforeSize > 0) {
beforeBuf = malloc(beforeSize);
if (!beforeBuf) {
if(isNew) {
Icc->TagCount--;
}
free(ptr);
J2dRlsTraceLn(J2D_TRACE_ERROR,
"_cmsModifyTagData: beforeBuf == NULL");
return FALSE;
}
Icc->Seek(Icc, Icc->TagOffsets[0]);
Icc->Read(beforeBuf, beforeSize, 1, Icc);
}
if (afterSize > 0) {
afterBuf = malloc(afterSize);
if (!afterBuf) {
free(ptr);
if(isNew) {
Icc->TagCount--;
}
if (beforeSize > 0) {
free(beforeBuf);
}
J2dRlsTraceLn(J2D_TRACE_ERROR,
"_cmsModifyTagData: afterBuf == NULL");
return FALSE;
}
Icc->Seek(Icc, Icc->TagOffsets[idx+1]);
Icc->Read(afterBuf, afterSize, 1, Icc);
}
CopyMemory(ptr, data, size);
Icc->TagSizes[idx] = size;
Icc->TagNames[idx] = sig;
if (Icc->TagPtrs[idx]) {
free(Icc->TagPtrs[idx]);
}
Icc->TagPtrs[idx] = ptr;
if (isNew) {
Icc->TagOffsets[idx] = profileSize;
}
/* Update the profile size in the header */
profileSize += delta;
Icc->Seek(Icc, 0);
temp = TransportValue32(profileSize);
Icc->Write(Icc, sizeof(icUInt32Number), &temp);
/* Shrink Icc, if needed. */
if (delta < 0) {
if (!Icc->Grow(Icc, delta)) {
free(ptr);
if(isNew) {
Icc->TagCount--;
}
J2dRlsTraceLn(J2D_TRACE_ERROR,
"_cmsModifyTagData: Icc->Grow() == FALSE");
return FALSE;
}
}
/* Adjust tag offsets: if the tag is new, we must account
for the new tag table entry; otherwise, only those tags after
the modified tag are changed (by delta) */
if (isNew) {
for (i = 0; i < Icc->TagCount; ++i) {
Icc->TagOffsets[i] += sizeof(icTag);
}
} else {
for (i = idx+1; i < Icc->TagCount; ++i) {
Icc->TagOffsets[i] += delta;
}
}
/* Write out a new tag table */
count = 0;
for (i = 0; i < Icc->TagCount; ++i) {
if (Icc->TagNames[i] != 0) {
++count;
}
}
Icc->Seek(Icc, sizeof(icHeader));
temp = TransportValue32(count);
Icc->Write(Icc, sizeof(icUInt32Number), &temp);
for (i = 0; i < Icc->TagCount; ++i) {
if (Icc->TagNames[i] != 0) {
icTag tag;
tag.sig = TransportValue32(Icc->TagNames[i]);
tag.offset = TransportValue32((icInt32Number) Icc->TagOffsets[i]);
tag.size = TransportValue32((icInt32Number) Icc->TagSizes[i]);
Icc->Write(Icc, sizeof(icTag), &tag);
}
}
/* Write unchanged data before the modified tag */
if (beforeSize > 0) {
Icc->Write(Icc, beforeSize, beforeBuf);
free(beforeBuf);
}
/* Write modified tag data */
Icc->Write(Icc, size, data);
if (size % 4) {
Icc->Write(Icc, 4 - (size % 4), padChars);
}
/* Write unchanged data after the modified tag */
if (afterSize > 0) {
Icc->Write(Icc, afterSize, afterBuf);
free(afterBuf);
}
return TRUE;
// load raw profile data into the buffer
if (cmsSaveProfileToMem(pf, pfBuffer, &pfSize)) {
memcpy(pBuffer, pfBuffer, sizeof(cmsICCHeader));
status = TRUE;
}
free(pfBuffer);
return status;
}
cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize)
{
cmsICCHeader pfHeader = { 0 };
if (pBuffer == NULL || bufferSize < sizeof(cmsICCHeader)) {
return FALSE;
}
memcpy(&pfHeader, pBuffer, sizeof(cmsICCHeader));
// now set header fields, which we can access using the lcms2 public API
cmsSetHeaderFlags(pf, pfHeader.flags);
cmsSetHeaderManufacturer(pf, pfHeader.manufacturer);
cmsSetHeaderModel(pf, pfHeader.model);
cmsSetHeaderAttributes(pf, pfHeader.attributes);
cmsSetHeaderProfileID(pf, (cmsUInt8Number*)&(pfHeader.profileID));
cmsSetHeaderRenderingIntent(pf, pfHeader.renderingIntent);
cmsSetPCS(pf, pfHeader.pcs);
cmsSetColorSpace(pf, pfHeader.colorSpace);
cmsSetDeviceClass(pf, pfHeader.deviceClass);
cmsSetEncodedICCversion(pf, pfHeader.version);
return TRUE;
}

View File

@ -27,9 +27,10 @@
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
// Little Color Management System
// Copyright (c) 1998-2010 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"),
@ -48,69 +49,65 @@
// 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 "lcms2_internal.h"
// 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];
cmsFloat64Number XYZ[3];
cmsFloat64Number RGB[3];
cmsFloat64Number RGBc[3];
cmsFloat64Number RGBp[3];
cmsFloat64Number RGBpa[3];
cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M;
cmsFloat64Number abC[2];
cmsFloat64Number abs[2];
cmsFloat64Number abM[2];
} CAM02COLOR, *LPCAM02COLOR;
} CAM02COLOR;
typedef struct {
CAM02COLOR adoptedWhite;
double LA, Yb;
double F, c, Nc;
int surround;
double n, Nbb, Ncb, z, FL, D;
cmsFloat64Number LA, Yb;
cmsFloat64Number F, c, Nc;
cmsUInt32Number surround;
cmsFloat64Number n, Nbb, Ncb, z, FL, D;
} cmsCIECAM02, *LPcmsCIECAM02;
cmsContext ContextID;
} cmsCIECAM02;
static
double compute_n(LPcmsCIECAM02 pMod)
cmsFloat64Number compute_n(cmsCIECAM02* pMod)
{
return(pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
}
static
double compute_z(LPcmsCIECAM02 pMod)
cmsFloat64Number compute_z(cmsCIECAM02* pMod)
{
return(1.48 + pow(pMod -> n, 0.5));
return (1.48 + pow(pMod -> n, 0.5));
}
static
double computeNbb(LPcmsCIECAM02 pMod)
cmsFloat64Number computeNbb(cmsCIECAM02* pMod)
{
return(0.725 * pow((1.0 / pMod -> n), 0.2));
return (0.725 * pow((1.0 / pMod -> n), 0.2));
}
static
double computeFL(LPcmsCIECAM02 pMod)
cmsFloat64Number computeFL(cmsCIECAM02* pMod)
{
double k, FL;
cmsFloat64Number k, FL;
k = 1.0 / ((5.0 * pMod->LA) + 1.0);
FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 *
@ -121,9 +118,9 @@ double computeFL(LPcmsCIECAM02 pMod)
}
static
double computeD(LPcmsCIECAM02 pMod)
cmsFloat64Number computeD(cmsCIECAM02* pMod)
{
double D;
cmsFloat64Number D;
D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0)));
@ -142,9 +139,10 @@ CAM02COLOR XYZtoCAT02(CAM02COLOR clr)
}
static
CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)
{
int i;
cmsUInt32Number i;
for (i = 0; i < 3; i++) {
clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] *
(pMod->D / pMod -> adoptedWhite.RGB[i])) +
@ -156,11 +154,9 @@ CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
static
CAM02COLOR CAT02toHPE (CAM02COLOR clr)
CAM02COLOR CAT02toHPE(CAM02COLOR clr)
{
double M[9];
cmsFloat64Number 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));
@ -180,10 +176,10 @@ CAM02COLOR CAT02toHPE (CAM02COLOR clr)
}
static
CAM02COLOR NonlinearCompression(CAM02COLOR clr, LPcmsCIECAM02 pMod)
CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod)
{
int i;
double temp;
cmsUInt32Number i;
cmsFloat64Number temp;
for (i = 0; i < 3; i++) {
if (clr.RGBp[i] < 0) {
@ -204,9 +200,9 @@ CAM02COLOR NonlinearCompression(CAM02COLOR clr, LPcmsCIECAM02 pMod)
}
static
CAM02COLOR ComputeCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
{
double a, b, temp, e, t, r2d, d2r;
cmsFloat64Number 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;
@ -274,10 +270,10 @@ CAM02COLOR ComputeCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
static
CAM02COLOR InverseCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
{
double t, e, p1, p2, p3, p4, p5, hr, d2r;
cmsFloat64Number 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) *
@ -327,10 +323,10 @@ CAM02COLOR InverseCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
}
static
CAM02COLOR InverseNonlinearity(CAM02COLOR clr, LPcmsCIECAM02 pMod)
CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod)
{
int i;
double c1;
cmsUInt32Number i;
cmsFloat64Number c1;
for (i = 0; i < 3; i++) {
if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1;
@ -347,7 +343,7 @@ CAM02COLOR InverseNonlinearity(CAM02COLOR clr, LPcmsCIECAM02 pMod)
static
CAM02COLOR HPEtoCAT02(CAM02COLOR clr)
{
double M[9];
cmsFloat64Number M[9];
M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950));
M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054));
@ -362,19 +358,19 @@ CAM02COLOR HPEtoCAT02(CAM02COLOR clr)
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);
return clr;
}
static
CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod)
{
int i;
cmsUInt32Number 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);
return clr;
}
@ -385,23 +381,21 @@ CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
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);
return clr;
}
LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC)
{
LPcmsCIECAM02 lpMod;
cmsCIECAM02* lpMod;
_cmsAssert(pVC != NULL);
if((lpMod = (LPcmsCIECAM02) _cmsMalloc(sizeof(cmsCIECAM02))) == NULL) {
return (LCMSHANDLE) NULL;
if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {
return NULL;
}
ZeroMemory(lpMod, sizeof(cmsCIECAM02));
lpMod ->ContextID = ContextID;
lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
@ -414,36 +408,30 @@ LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
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 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 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;
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;
default:
// Average surround
lpMod -> F = 1.0;
lpMod -> c = 0.69;
lpMod -> Nc = 1.0;
}
lpMod -> n = compute_n(lpMod);
@ -451,10 +439,8 @@ LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
lpMod -> Nbb = computeNbb(lpMod);
lpMod -> FL = computeFL(lpMod);
if (lpMod -> D == D_CALCULATE ||
lpMod -> D == D_CALCULATE_DISCOUNT) {
lpMod -> D = computeD(lpMod);
if (lpMod -> D == D_CALCULATE) {
lpMod -> D = computeD(lpMod);
}
lpMod -> Ncb = lpMod -> Nbb;
@ -464,21 +450,26 @@ LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
return (LCMSHANDLE) lpMod;
return (cmsHANDLE) lpMod;
}
void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel)
void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel)
{
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
if (lpMod) _cmsFree(lpMod);
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
if (lpMod) _cmsFree(lpMod ->ContextID, lpMod);
}
void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut)
void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut)
{
CAM02COLOR clr;
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
_cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL);
clr.XYZ[0] = pIn ->X;
clr.XYZ[1] = pIn ->Y;
@ -495,11 +486,14 @@ void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh
pOut ->h = clr.h;
}
void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut)
void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut)
{
CAM02COLOR clr;
LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
_cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL);
clr.J = pIn -> J;
clr.C = pIn -> C;
@ -514,6 +508,5 @@ void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ
pOut ->X = clr.XYZ[0];
pOut ->Y = clr.XYZ[1];
pOut ->Z = clr.XYZ[2];
}

View File

@ -1,750 +0,0 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//
// 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) _cmsFree(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, &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
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,10 @@
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
// Little Color Management System
// Copyright (c) 1998-2010 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"),
@ -48,92 +49,399 @@
// 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 "lcms2_internal.h"
#include "lcms.h"
// I am so tired about incompatibilities on those functions that here are some replacements
// that hopefully would be fully portable.
// 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)
// compare two strings ignoring case
int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
{
int nOld = nDoAbort;
nDoAbort = nAction;
register const unsigned char *us1 = (const unsigned char *)s1,
*us2 = (const unsigned char *)s2;
return nOld;
while (toupper(*us1) == toupper(*us2++))
if (*us1++ == '\0')
return (0);
return (toupper(*us1) - toupper(*--us2));
}
void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn)
// long int because C99 specifies ftell in such way (7.19.9.2)
long int CMSEXPORT cmsfilelength(FILE* f)
{
UserErrorHandler = Fn;
long int n;
if (fseek(f, 0, SEEK_END) != 0) {
return -1;
}
n = ftell(f);
fseek(f, 0, SEEK_SET);
return n;
}
// Memory handling ------------------------------------------------------------------
//
// This is the interface to low-level memory management routines. By default a simple
// wrapping to malloc/free/realloc is provided, although there is a limit on the max
// amount of memoy that can be reclaimed. This is mostly as a safety feature to
// prevent bogus or malintentionated code to allocate huge blocks that otherwise lcms
// would never need.
#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U))
// User may override this behaviour by using a memory plug-in, which basically replaces
// the default memory management functions. In this case, no check is performed and it
// is up to the plug-in writter to keep in the safe side. There are only three functions
// required to be implemented: malloc, realloc and free, although the user may want to
// replace the optional mallocZero, calloc and dup as well.
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin);
// *********************************************************************************
// This is the default memory allocation function. It does a very coarse
// check of amout of memory, just to prevent exploits
static
void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size)
{
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum
return (void*) malloc(size);
cmsUNUSED_PARAMETER(ContextID);
}
// Generic allocate & zero
static
void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size)
{
void *pt = _cmsMalloc(ContextID, size);
if (pt == NULL) return NULL;
memset(pt, 0, size);
return pt;
}
// Default error handler
void cmsSignalError(int ErrorCode, const char *ErrorText, ...)
// The default free function. The only check proformed is against NULL pointers
static
void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr)
{
va_list args;
// free(NULL) is defined a no-op by C99, therefore it is safe to
// avoid the check, but it is here just in case...
if (nDoAbort == LCMS_ERROR_IGNORE) return;
if (Ptr) free(Ptr);
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
cmsUNUSED_PARAMETER(ContextID);
}
// The default realloc function. Again it check for exploits. If Ptr is NULL,
// realloc behaves the same way as malloc and allocates a new block of size bytes.
static
void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
{
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb
return realloc(Ptr, size);
cmsUNUSED_PARAMETER(ContextID);
}
// The default calloc function. Allocates an array of num elements, each one of size bytes
// all memory is initialized to zero.
static
void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
{
cmsUInt32Number Total = num * size;
// Check for overflow
if (Total < num || Total < size) {
return NULL;
}
if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb
return _cmsMallocZero(ContextID, Total);
}
// Generic block duplication
static
void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size)
{
void* mem;
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb
mem = _cmsMalloc(ContextID, size);
if (mem != NULL && Org != NULL)
memmove(mem, Org, size);
return mem;
}
// Pointers to malloc and _cmsFree functions in current environment
static void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocDefaultFn;
static void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocZeroDefaultFn;
static void (* FreePtr)(cmsContext ContextID, void *Ptr) = _cmsFreeDefaultFn;
static void * (* ReallocPtr)(cmsContext ContextID, void *Ptr, cmsUInt32Number NewSize) = _cmsReallocDefaultFn;
static void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)= _cmsCallocDefaultFn;
static void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size) = _cmsDupDefaultFn;
// Plug-in replacement entry
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase *Data)
{
cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data;
// NULL forces to reset to defaults
if (Data == NULL) {
MallocPtr = _cmsMallocDefaultFn;
MallocZeroPtr= _cmsMallocZeroDefaultFn;
FreePtr = _cmsFreeDefaultFn;
ReallocPtr = _cmsReallocDefaultFn;
CallocPtr = _cmsCallocDefaultFn;
DupPtr = _cmsDupDefaultFn;
return TRUE;
}
// Check for required callbacks
if (Plugin -> MallocPtr == NULL ||
Plugin -> FreePtr == NULL ||
Plugin -> ReallocPtr == NULL) return FALSE;
// Set replacement functions
MallocPtr = Plugin -> MallocPtr;
FreePtr = Plugin -> FreePtr;
ReallocPtr = Plugin -> ReallocPtr;
if (Plugin ->MallocZeroPtr != NULL) MallocZeroPtr = Plugin ->MallocZeroPtr;
if (Plugin ->CallocPtr != NULL) CallocPtr = Plugin -> CallocPtr;
if (Plugin ->DupPtr != NULL) DupPtr = Plugin -> DupPtr;
return TRUE;
}
// Generic allocate
void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size)
{
return MallocPtr(ContextID, size);
}
// Generic allocate & zero
void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size)
{
return MallocZeroPtr(ContextID, size);
}
// Generic calloc
void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
{
return CallocPtr(ContextID, num, size);
}
// Generic reallocate
void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
{
return ReallocPtr(ContextID, Ptr, size);
}
// Generic free memory
void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr)
{
if (Ptr != NULL) FreePtr(ContextID, Ptr);
}
// Generic block duplication
void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size)
{
return DupPtr(ContextID, Org, size);
}
// ********************************************************************************************
// Sub allocation takes care of many pointers of small size. The memory allocated in
// this way have be freed at once. Next function allocates a single chunk for linked list
// I prefer this method over realloc due to the big inpact on xput realloc may have if
// memory is being swapped to disk. This approach is safer (although thats not true on any platform)
static
_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
{
_cmsSubAllocator_chunk* chunk;
// Create the container
chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk));
if (chunk == NULL) return NULL;
// Initialize values
chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial);
if (chunk ->Block == NULL) {
// Something went wrong
_cmsFree(ContextID, chunk);
return NULL;
}
// 20K by default
if (Initial == 0)
Initial = 20*1024;
chunk ->BlockSize = Initial;
chunk ->Used = 0;
chunk ->next = NULL;
return chunk;
}
// The suballocated is nothing but a pointer to the first element in the list. We also keep
// the thread ID in this structure.
_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial)
{
_cmsSubAllocator* sub;
// Create the container
sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator));
if (sub == NULL) return NULL;
sub ->ContextID = ContextID;
sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial);
if (sub ->h == NULL) {
_cmsFree(ContextID, sub);
return NULL;
}
return sub;
}
// Get rid of whole linked list
void _cmsSubAllocDestroy(_cmsSubAllocator* sub)
{
_cmsSubAllocator_chunk *chunk, *n;
for (chunk = sub ->h; chunk != NULL; chunk = n) {
n = chunk->next;
if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block);
_cmsFree(sub ->ContextID, chunk);
}
// Free the header
_cmsFree(sub ->ContextID, sub);
}
// Get a pointer to small memory block.
void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
{
cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used;
cmsUInt8Number* ptr;
size = _cmsALIGNLONG(size);
// Check for memory. If there is no room, allocate a new chunk of double memory size.
if (size > Free) {
_cmsSubAllocator_chunk* chunk;
cmsUInt32Number newSize;
newSize = sub -> h ->BlockSize * 2;
if (newSize < size) newSize = size;
chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize);
if (chunk == NULL) return NULL;
// Link list
chunk ->next = sub ->h;
sub ->h = chunk;
}
ptr = sub -> h ->Block + sub -> h ->Used;
sub -> h -> Used += size;
return (void*) ptr;
}
// Error logging ******************************************************************
// There is no error handling at all. When a funtion fails, it returns proper value.
// For example, all create functions does return NULL on failure. Other return FALSE
// It may be interesting, for the developer, to know why the function is failing.
// for that reason, lcms2 does offer a logging function. This function does recive
// a ENGLISH string with some clues on what is going wrong. You can show this
// info to the end user, or just create some sort of log.
// The logging function should NOT terminate the program, as this obviously can leave
// resources. It is the programmer's responsability to check each function return code
// to make sure it didn't fail.
// Error messages are limited to MAX_ERROR_MESSAGE_LEN
#define MAX_ERROR_MESSAGE_LEN 1024
// ---------------------------------------------------------------------------------------------------------
// This is our default log error
static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text);
// The current handler in actual environment
static cmsLogErrorHandlerFunction LogErrorHandler = DefaultLogErrorHandlerFunction;
// The default error logger does nothing.
static
void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
{
// fprintf(stderr, "[lcms]: %s\n", Text);
// fflush(stderr);
cmsUNUSED_PARAMETER(ContextID);
cmsUNUSED_PARAMETER(ErrorCode);
cmsUNUSED_PARAMETER(Text);
}
// Change log error
void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn)
{
if (Fn == NULL)
LogErrorHandler = DefaultLogErrorHandlerFunction;
else
LogErrorHandler = Fn;
}
// Log an error
// ErrorText is a text holding an english description of error.
void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...)
{
va_list args;
char Buffer[MAX_ERROR_MESSAGE_LEN];
va_start(args, ErrorText);
vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args);
va_end(args);
// Call handler
LogErrorHandler(ContextID, ErrorCode, Buffer);
}
// Utility function to print signatures
void _cmsTagSignature2String(char String[5], cmsTagSignature sig)
{
cmsUInt32Number be;
// Convert to big endian
be = _cmsAdjustEndianess32((cmsUInt32Number) sig);
// Move chars
memmove(String, &be, 4);
// Make sure of terminator
String[4] = 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,411 +0,0 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//
// 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;
if (Matrix == NULL) return NULL;
for (i=0; i < 3; i++) {
if (Tables[i] == NULL) return NULL;
}
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]) _cmsFree(MatShaper ->L[i]);
if (MatShaper -> L2[i]) _cmsFree(MatShaper ->L2[i]);
}
_cmsFree(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);
}

View File

@ -0,0 +1,346 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 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.
//
//---------------------------------------------------------------------------------
#include "lcms2_internal.h"
#ifdef CMS_USE_BIG_ENDIAN
static
void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs)
{
do {
cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf);
*(cmsUInt32Number *) buf = t;
buf += sizeof(cmsUInt32Number);
} while (--longs);
}
#else
#define byteReverse(buf, len)
#endif
typedef struct {
cmsUInt32Number buf[4];
cmsUInt32Number bits[2];
cmsUInt8Number in[64];
cmsContext ContextID;
} _cmsMD5;
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
static
void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16])
{
register cmsUInt32Number a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
// Create a MD5 object
static
cmsHANDLE MD5alloc(cmsContext ContextID)
{
_cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5));
if (ctx == NULL) return NULL;
ctx ->ContextID = ContextID;
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
return (cmsHANDLE) ctx;
}
static
void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len)
{
_cmsMD5* ctx = (_cmsMD5*) Handle;
cmsUInt32Number t;
t = ctx->bits[0];
if ((ctx->bits[0] = t + (len << 3)) < t)
ctx->bits[1]++;
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f;
if (t) {
cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t;
t = 64 - t;
if (len < t) {
memmove(p, buf, len);
return;
}
memmove(p, buf, t);
byteReverse(ctx->in, 16);
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
buf += t;
len -= t;
}
while (len >= 64) {
memmove(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
buf += 64;
len -= 64;
}
memmove(ctx->in, buf, len);
}
// Destroy the object and return the checksum
static
void MD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle)
{
_cmsMD5* ctx = (_cmsMD5*) Handle;
cmsUInt32Number count;
cmsUInt8Number *p;
count = (ctx->bits[0] >> 3) & 0x3F;
p = ctx->in + count;
*p++ = 0x80;
count = 64 - 1 - count;
if (count < 8) {
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
memset(ctx->in, 0, 56);
} else {
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0];
((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1];
MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in);
byteReverse((cmsUInt8Number *) ctx->buf, 4);
memmove(ProfileID ->ID8, ctx->buf, 16);
_cmsFree(ctx ->ContextID, ctx);
}
// Assuming io points to an ICC profile, compute and store MD5 checksum
// In the header, rendering intentent, attributes and ID should be set to zero
// before computing MD5 checksum (per 6.1.13 in ICC spec)
cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile)
{
cmsContext ContextID;
cmsUInt32Number BytesNeeded;
cmsUInt8Number* Mem = NULL;
cmsHANDLE MD5 = NULL;
_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
_cmsICCPROFILE Keep;
_cmsAssert(hProfile != NULL);
ContextID = cmsGetProfileContextID(hProfile);
// Save a copy of the profile header
memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
// Set RI, attributes and ID
memset(&Icc ->attributes, 0, sizeof(Icc ->attributes));
Icc ->RenderingIntent = 0;
memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID));
// Compute needed storage
if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error;
// Allocate memory
Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded);
if (Mem == NULL) goto Error;
// Save to temporary storage
if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error;
// Create MD5 object
MD5 = MD5alloc(ContextID);
if (MD5 == NULL) goto Error;
// Add all bytes
MD5add(MD5, Mem, BytesNeeded);
// Temp storage is no longer needed
_cmsFree(ContextID, Mem);
// Restore header
memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
// And store the ID
MD5finish(&Icc ->ProfileID, MD5);
return TRUE;
Error:
// Free resources as something went wrong
if (MD5 != NULL) _cmsFree(ContextID, MD5);
if (Mem != NULL) _cmsFree(ContextID, Mem);
memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
return FALSE;
}

View File

@ -27,9 +27,10 @@
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
// Little Color Management System
// Copyright (c) 1998-2010 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"),
@ -48,447 +49,105 @@
// 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"
#include "lcms2_internal.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);
#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;}
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)
// Initiate a vector
void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z)
{
ASM {
mov eax, ss:a
mov edx, ss:b
imul edx
add eax, 0x8000
adc edx, 0
shrd eax, edx, 16
}
RET(_EAX);
r -> n[VX] = x;
r -> n[VY] = y;
r -> n[VZ] = z;
}
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)
// Vector substraction
void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* 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)
void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* 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];
}
// Vector dot product
cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v)
{
return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ];
}
// The vector size
double VEC3length(LPVEC3 a)
// Euclidean length
cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* 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)
cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* 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];
cmsFloat64Number d1 = a ->n[VX] - b ->n[VX];
cmsFloat64Number d2 = a ->n[VY] - b ->n[VY];
cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ];
return sqrt(d1*d1 + d2*d2 + d3*d3);
}
// Identity
void MAT3identity(LPMAT3 a)
// 3x3 Identity
void CMSEXPORT _cmsMAT3identity(cmsMAT3* 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);
_cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0);
_cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0);
_cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0);
}
static
cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b)
{
return fabs(b - a) < (1.0 / 65535.0);
}
// Check if matrix is Identity. Allow a tolerance as %
LCMSBOOL MAT3isIdentity(LPWMAT3 a, double Tolerance)
cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a)
{
int i;
MAT3 Idd;
WMAT3 Idf;
cmsMAT3 Identity;
int i, j;
MAT3identity(&Idd);
MAT3toFix(&Idf, &Idd);
_cmsMAT3identity(&Identity);
for (i=0; i < 3; i++)
if (!VEC3equal(&a -> v[i], &Idf.v[i], Tolerance)) return FALSE;
return TRUE;
for (i=0; i < 3; i++)
for (j=0; j < 3; j++)
if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE;
return TRUE;
}
// Multiply two matrices
void MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b)
void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* 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));
_cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2));
_cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2));
_cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2));
#undef ROWCOL //(i, j)
}
@ -496,350 +155,50 @@ void MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b)
// Inverse of a matrix b = a^(-1)
// Gauss-Jordan elimination with partial pivoting
int MAT3inverse(LPMAT3 a, LPMAT3 b)
cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b)
{
register int i, j, max;
cmsFloat64Number det, c0, c1, c2;
MAT3identity(b);
c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1];
c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0];
c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0];
// 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
det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2;
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;
if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert
// Swap rows max and j in a and b to put pivot on diagonal
b -> v[0].n[0] = c0/det;
b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det;
b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det;
b -> v[1].n[0] = c1/det;
b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det;
b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det;
b -> v[2].n[0] = c2/det;
b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det;
b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det;
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;
return TRUE;
}
// Solve a system in the form Ax = b
LCMSBOOL MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b)
cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b)
{
MAT3 m, a_1;
cmsMAT3 m, a_1;
CopyMemory(&m, a, sizeof(MAT3));
memmove(&m, a, sizeof(cmsMAT3));
if (!MAT3inverse(&m, &a_1)) return FALSE; // Singular matrix
if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix
MAT3eval(x, &a_1, b);
_cmsMAT3eval(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)
// Evaluate a vector across a matrix
void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* 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);
}

View File

@ -27,9 +27,10 @@
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
// Little Color Management System
// Copyright (c) 1998-2010 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"),
@ -48,153 +49,725 @@
// 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 "lcms2_internal.h"
// Multilocalized unicode objects. That is an attempt to encapsulate i18n.
// Named color support
#include "lcms.h"
static
LPcmsNAMEDCOLORLIST GrowNamedColorList(LPcmsNAMEDCOLORLIST v, int ByElements)
// Allocates an empty multi localizad unicode object
cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
{
if (ByElements > v ->Allocated) {
cmsMLU* mlu;
LPcmsNAMEDCOLORLIST TheNewList;
int NewElements;
size_t size;
// nItems should be positive if given
if (nItems <= 0) nItems = 2;
if (v ->Allocated == 0)
NewElements = 64; // Initial guess
else
NewElements = v ->Allocated;
// Create the container
mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
if (mlu == NULL) return NULL;
while (ByElements > NewElements)
NewElements *= 2;
mlu ->ContextID = ContextID;
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;
_cmsFree(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");
// Create entry array
mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
if (mlu ->Entries == NULL) {
_cmsFree(ContextID, mlu);
return NULL;
}
ZeroMemory(v, size);
// Ok, keep indexes up to date
mlu ->AllocatedEntries = nItems;
mlu ->UsedEntries = 0;
v ->nColors = n;
v ->Allocated = n;
v ->Prefix[0] = 0;
v ->Suffix[0] = 0;
return v;
return mlu;
}
void cmsFreeNamedColorList(LPcmsNAMEDCOLORLIST v)
// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.
static
cmsBool GrowMLUpool(cmsMLU* mlu)
{
if (v == NULL) {
cmsSignalError(LCMS_ERRC_RECOVERABLE, "Couldn't free a NULL named color list");
return;
}
cmsUInt32Number size;
void *NewPtr;
_cmsFree(v);
}
// Sanity check
if (mlu == NULL) return FALSE;
LCMSBOOL cmsAppendNamedColor(cmsHTRANSFORM xform, const char* Name, WORD PCS[3], WORD Colorant[MAXCHANNELS])
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
LPcmsNAMEDCOLORLIST List;
int i;
if (mlu ->PoolSize == 0)
size = 256;
else
size = mlu ->PoolSize * 2;
if (v ->NamedColorList == NULL) return FALSE;
// Check for overflow
if (size < mlu ->PoolSize) return FALSE;
v ->NamedColorList = GrowNamedColorList(v ->NamedColorList, v->NamedColorList ->nColors + 1);
// Reallocate the pool
NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
if (NewPtr == NULL) return FALSE;
List = v ->NamedColorList;
for (i=0; i < MAXCHANNELS; i++)
List ->List[List ->nColors].DeviceColorant[i] = Colorant[i];
mlu ->MemPool = NewPtr;
mlu ->PoolSize = size;
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 ->List[List ->nColors].Name[MAX_PATH-1] = 0;
List ->nColors++;
return TRUE;
}
// Returns named color count
int LCMSEXPORT cmsNamedColorCount(cmsHTRANSFORM xform)
// Grows a ntry table for a MLU. Each time this function is called, table size is multiplied times two.
static
cmsBool GrowMLUtable(cmsMLU* mlu)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
int AllocatedEntries;
_cmsMLUentry *NewPtr;
if (v ->NamedColorList == NULL) return 0;
return v ->NamedColorList ->nColors;
// Sanity check
if (mlu == NULL) return FALSE;
AllocatedEntries = mlu ->AllocatedEntries * 2;
// Check for overflow
if (AllocatedEntries < mlu ->AllocatedEntries) return FALSE;
// Reallocate the memory
NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
if (NewPtr == NULL) return FALSE;
mlu ->Entries = NewPtr;
mlu ->AllocatedEntries = AllocatedEntries;
return TRUE;
}
LCMSBOOL LCMSEXPORT cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, char* Name, char* Prefix, char* Suffix)
// Search for a specific entry in the structure. Language and Country are used.
static
int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
int i;
if (v ->NamedColorList == NULL) return FALSE;
// Sanity check
if (mlu == NULL) return -1;
if (nColor < 0 || nColor >= cmsNamedColorCount(xform)) return FALSE;
// Iterate whole table
for (i=0; i < mlu ->UsedEntries; i++) {
if (Name) { strncpy(Name, v ->NamedColorList->List[nColor].Name, 31); Name[31] = 0; }
if (Prefix) { strncpy(Prefix, v ->NamedColorList->Prefix, 31); Prefix[31] = 0; }
if (Suffix) { strncpy(Suffix, v ->NamedColorList->Suffix, 31); Suffix[31] = 0; }
if (mlu ->Entries[i].Country == CountryCode &&
mlu ->Entries[i].Language == LanguageCode) return i;
}
return TRUE;
// Not found
return -1;
}
// Add a block of characters to the intended MLU. Language and country are specified.
// Only one entry for Language/country pair is allowed.
static
cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
{
cmsUInt32Number Offset;
cmsUInt8Number* Ptr;
// Sanity check
if (mlu == NULL) return FALSE;
// Is there any room available?
if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
if (!GrowMLUtable(mlu)) return FALSE;
}
// Only one ASCII string
if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed!
// Check for size
while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
if (!GrowMLUpool(mlu)) return FALSE;
}
Offset = mlu ->PoolUsed;
Ptr = (cmsUInt8Number*) mlu ->MemPool;
if (Ptr == NULL) return FALSE;
// Set the entry
memmove(Ptr + Offset, Block, size);
mlu ->PoolUsed += size;
mlu ->Entries[mlu ->UsedEntries].StrW = Offset;
mlu ->Entries[mlu ->UsedEntries].Len = size;
mlu ->Entries[mlu ->UsedEntries].Country = CountryCode;
mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;
mlu ->UsedEntries++;
return TRUE;
}
int LCMSEXPORT cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name)
// Add an ASCII entry.
cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
{
cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString)+1;
wchar_t* WStr;
cmsBool rc;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
if (mlu == NULL) return FALSE;
WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t));
if (WStr == NULL) return FALSE;
for (i=0; i < len; i++)
WStr[i] = (wchar_t) ASCIIString[i];
rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);
_cmsFree(mlu ->ContextID, WStr);
return rc;
}
// We don't need any wcs support library
static
cmsUInt32Number mywcslen(const wchar_t *s)
{
const wchar_t *p;
p = s;
while (*p)
p++;
return (cmsUInt32Number)(p - s);
}
// Add a wide entry
cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
{
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) Language);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) Country);
cmsUInt32Number len;
if (mlu == NULL) return FALSE;
if (WideString == NULL) return FALSE;
len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t);
return AddMLUBlock(mlu, len, WideString, Lang, Cntry);
}
// Duplicating a MLU is as easy as copying all members
cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)
{
cmsMLU* NewMlu = NULL;
// Duplicating a NULL obtains a NULL
if (mlu == NULL) return NULL;
NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
if (NewMlu == NULL) return NULL;
// Should never happen
if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
goto Error;
// Sanitize...
if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error;
memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
NewMlu ->UsedEntries = mlu ->UsedEntries;
// The MLU may be empty
if (mlu ->PoolUsed == 0) {
NewMlu ->MemPool = NULL;
}
else {
// It is not empty
NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed);
if (NewMlu ->MemPool == NULL) goto Error;
}
NewMlu ->PoolSize = mlu ->PoolUsed;
if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
NewMlu ->PoolUsed = mlu ->PoolUsed;
return NewMlu;
Error:
if (NewMlu != NULL) cmsMLUfree(NewMlu);
return NULL;
}
// Free any used memory
void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
{
if (mlu) {
if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);
_cmsFree(mlu ->ContextID, mlu);
}
}
// The algorithm first searches for an exact match of country and language, if not found it uses
// the Language. If none is found, first entry is used instead.
static
const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
cmsUInt32Number *len,
cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
{
int i;
int Best = -1;
_cmsMLUentry* v;
if (mlu == NULL) return NULL;
if (mlu -> AllocatedEntries <= 0) return NULL;
for (i=0; i < mlu ->UsedEntries; i++) {
v = mlu ->Entries + i;
if (v -> Language == LanguageCode) {
if (Best == -1) Best = i;
if (v -> Country == CountryCode) {
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
if (len != NULL) *len = v ->Len;
return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match
}
}
}
// No string found. Return First one
if (Best == -1)
Best = 0;
v = mlu ->Entries + Best;
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
if (len != NULL) *len = v ->Len;
return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
}
// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize)
{
const wchar_t *Wide;
cmsUInt32Number StrLen = 0;
cmsUInt32Number ASCIIlen, i;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
// Sanitize
if (mlu == NULL) return 0;
// Get WideChar
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
if (Wide == NULL) return 0;
ASCIIlen = StrLen / sizeof(wchar_t);
// Maybe we want only to know the len?
if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end
// No buffer size means no data
if (BufferSize <= 0) return 0;
// Some clipping may be required
if (BufferSize < ASCIIlen + 1)
ASCIIlen = BufferSize - 1;
// Precess each character
for (i=0; i < ASCIIlen; i++) {
if (Wide[i] == 0)
Buffer[i] = 0;
else
Buffer[i] = (char) Wide[i];
}
// We put a termination "\0"
Buffer[ASCIIlen] = 0;
return ASCIIlen + 1;
}
// Obtain a wide representation of the MLU, on depending on current locale settings
cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
wchar_t* Buffer, cmsUInt32Number BufferSize)
{
const wchar_t *Wide;
cmsUInt32Number StrLen = 0;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
// Sanitize
if (mlu == NULL) return 0;
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
if (Wide == NULL) return 0;
// Maybe we want only to know the len?
if (Buffer == NULL) return StrLen + sizeof(wchar_t);
// No buffer size means no data
if (BufferSize <= 0) return 0;
// Some clipping may be required
if (BufferSize < StrLen + sizeof(wchar_t))
StrLen = BufferSize - + sizeof(wchar_t);
memmove(Buffer, Wide, StrLen);
Buffer[StrLen / sizeof(wchar_t)] = 0;
return StrLen + sizeof(wchar_t);
}
// Get also the language and country
CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
char ObtainedLanguage[3], char ObtainedCountry[3])
{
const wchar_t *Wide;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
cmsUInt16Number ObtLang, ObtCode;
// Sanitize
if (mlu == NULL) return FALSE;
Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
if (Wide == NULL) return FALSE;
// Get used language and code
*(cmsUInt16Number *)ObtainedLanguage = _cmsAdjustEndianess16(ObtLang);
*(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode);
ObtainedLanguage[2] = ObtainedCountry[2] = 0;
return TRUE;
}
// Named color lists --------------------------------------------------------------------------------------------
// Grow the list to keep at least NumElements
static
cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v)
{
cmsUInt32Number size;
_cmsNAMEDCOLOR * NewPtr;
if (v == NULL) return FALSE;
if (v ->Allocated == 0)
size = 64; // Initial guess
else
size = v ->Allocated * 2;
NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
if (NewPtr == NULL)
return FALSE;
v ->List = NewPtr;
v ->Allocated = size;
return TRUE;
}
// Allocate a list for n elements
cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
{
cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
if (v == NULL) return NULL;
v ->List = NULL;
v ->nColors = 0;
v ->ContextID = ContextID;
while (v -> Allocated < n)
GrowNamedColorList(v);
strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix));
strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix));
v -> ColorantCount = ColorantCount;
return v;
}
// Free a list
void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)
{
if (v ->List) _cmsFree(v ->ContextID, v ->List);
if (v) _cmsFree(v ->ContextID, v);
}
cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
{
cmsNAMEDCOLORLIST* NewNC;
if (v == NULL) return NULL;
NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);
if (NewNC == NULL) return NULL;
// For really large tables we need this
while (NewNC ->Allocated < v ->Allocated)
GrowNamedColorList(NewNC);
memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
NewNC ->ColorantCount = v ->ColorantCount;
memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
NewNC ->nColors = v ->nColors;
return NewNC;
}
// Append a color to a list. List pointer may change if reallocated
cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,
const char* Name,
cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])
{
cmsUInt32Number i;
if (NamedColorList == NULL) return FALSE;
if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {
if (!GrowNamedColorList(NamedColorList)) return FALSE;
}
for (i=0; i < NamedColorList ->ColorantCount; i++)
NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL? 0 : Colorant[i];
for (i=0; i < 3; i++)
NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i];
if (Name != NULL)
strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name,
sizeof(NamedColorList ->List[NamedColorList ->nColors].Name));
else
NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
NamedColorList ->nColors++;
return TRUE;
}
// Returns number of elements
cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList)
{
if (NamedColorList == NULL) return 0;
return NamedColorList ->nColors;
}
// Info aboout a given color
cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
char* Name,
char* Prefix,
char* Suffix,
cmsUInt16Number* PCS,
cmsUInt16Number* Colorant)
{
if (NamedColorList == NULL) return FALSE;
if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE;
if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
if (PCS)
memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));
if (Colorant)
memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,
sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);
return TRUE;
}
// Search for a given color name (no prefix or suffix)
cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
{
_LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
int i, n;
if (v ->NamedColorList == NULL) return -1;
if (NamedColorList == NULL) return -1;
n = cmsNamedColorCount(NamedColorList);
for (i=0; i < n; i++) {
if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0)
return i;
}
n = cmsNamedColorCount(xform);
for (i=0; i < n; i++) {
if (stricmp(Name, v ->NamedColorList->List[i].Name) == 0)
return i;
}
return -1;
}
return -1;
// MPE support -----------------------------------------------------------------------------------------------------------------
static
void FreeNamedColorList(cmsStage* mpe)
{
cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
cmsFreeNamedColorList(List);
}
static
void* DupNamedColorList(cmsStage* mpe)
{
cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
return cmsDupNamedColorList(List);
}
static
void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
{
cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
cmsUInt32Number j;
if (index >= NamedColorList-> nColors) {
cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
}
else {
for (j=0; j < NamedColorList ->ColorantCount; j++)
Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);
}
}
// Named color lookup element
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList)
{
return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
cmsSigNamedColorElemType,
1, 3,
EvalNamedColor,
DupNamedColorList,
FreeNamedColorList,
cmsDupNamedColorList(NamedColorList));
}
// Retrieve the named color list from a transform. Should be first element in the LUT
cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
{
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
cmsStage* mpe = v ->Lut->Elements;
if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
return (cmsNAMEDCOLORLIST*) mpe ->Data;
}
// Profile sequence description routines -------------------------------------------------------------------------------------
cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)
{
cmsSEQ* Seq;
cmsUInt32Number i;
if (n == 0) return NULL;
// In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
// in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
if (n > 255) return NULL;
Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));
if (Seq == NULL) return NULL;
Seq -> ContextID = ContextID;
Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
Seq -> n = n;
for (i=0; i < n; i++) {
Seq -> seq[i].Manufacturer = NULL;
Seq -> seq[i].Model = NULL;
Seq -> seq[i].Description = NULL;
}
return Seq;
}
void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq)
{
cmsUInt32Number i;
for (i=0; i < pseq ->n; i++) {
if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);
if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);
if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);
}
if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq);
_cmsFree(pseq -> ContextID, pseq);
}
cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)
{
cmsSEQ *NewSeq;
cmsUInt32Number i;
if (pseq == NULL)
return NULL;
NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));
if (NewSeq == NULL) return NULL;
NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));
if (NewSeq ->seq == NULL) goto Error;
NewSeq -> ContextID = pseq ->ContextID;
NewSeq -> n = pseq ->n;
for (i=0; i < pseq->n; i++) {
memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg;
NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;
memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));
NewSeq ->seq[i].technology = pseq ->seq[i].technology;
NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer);
NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model);
NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description);
}
return NewSeq;
Error:
cmsFreeProfileSequenceDescription(NewSeq);
return NULL;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,641 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 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.
//
//---------------------------------------------------------------------------------
//
#include "lcms2_internal.h"
// ----------------------------------------------------------------------------------
// Encoding & Decoding support functions
// ----------------------------------------------------------------------------------
// Little-Endian to Big-Endian
// Adjust a word value after being readed/ before being written from/to an ICC profile
cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word)
{
#ifndef CMS_USE_BIG_ENDIAN
cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
cmsUInt8Number tmp;
tmp = pByte[0];
pByte[0] = pByte[1];
pByte[1] = tmp;
#endif
return Word;
}
// Transports to properly encoded values - note that icc profiles does use big endian notation.
// 1 2 3 4
// 4 3 2 1
cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord)
{
#ifndef CMS_USE_BIG_ENDIAN
cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
cmsUInt8Number temp1;
cmsUInt8Number temp2;
temp1 = *pByte++;
temp2 = *pByte++;
*(pByte-1) = *pByte;
*pByte++ = temp2;
*(pByte-3) = *pByte;
*pByte = temp1;
#endif
return DWord;
}
// 1 2 3 4 5 6 7 8
// 8 7 6 5 4 3 2 1
void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number QWord)
{
#ifndef CMS_USE_BIG_ENDIAN
cmsUInt8Number* pIn = (cmsUInt8Number*) &QWord;
cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
_cmsAssert(Result != NULL);
pOut[7] = pIn[0];
pOut[6] = pIn[1];
pOut[5] = pIn[2];
pOut[4] = pIn[3];
pOut[3] = pIn[4];
pOut[2] = pIn[5];
pOut[1] = pIn[6];
pOut[0] = pIn[7];
#else
_cmsAssert(Result != NULL);
*Result = QWord;
#endif
}
// Auxiliar -- read 8, 16 and 32-bit numbers
cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n)
{
cmsUInt8Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
return FALSE;
if (n != NULL) *n = tmp;
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
{
cmsUInt16Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
return FALSE;
if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
{
cmsUInt32Number i;
_cmsAssert(io != NULL);
for (i=0; i < n; i++) {
if (Array != NULL) {
if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
}
else {
if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
}
}
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
return FALSE;
if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
return TRUE;
}
cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsFloat32Number), 1) != 1)
return FALSE;
if (n != NULL) {
tmp = _cmsAdjustEndianess32(tmp);
*n = *(cmsFloat32Number*) &tmp;
}
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
{
cmsUInt64Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
return FALSE;
if (n != NULL) _cmsAdjustEndianess64(n, tmp);
return TRUE;
}
cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
return FALSE;
if (n != NULL) {
*n = _cms15Fixed16toDouble(_cmsAdjustEndianess32(tmp));
}
return TRUE;
}
// Jun-21-2000: Some profiles (those that comes with W2K) comes
// with the media white (media black?) x 100. Add a sanity check
static
void NormalizeXYZ(cmsCIEXYZ* Dest)
{
while (Dest -> X > 2. &&
Dest -> Y > 2. &&
Dest -> Z > 2.) {
Dest -> X /= 10.;
Dest -> Y /= 10.;
Dest -> Z /= 10.;
}
}
cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
{
cmsEncodedXYZNumber xyz;
_cmsAssert(io != NULL);
if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
if (XYZ != NULL) {
XYZ->X = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.X));
XYZ->Y = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Y));
XYZ->Z = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Z));
NormalizeXYZ(XYZ);
}
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
{
_cmsAssert(io != NULL);
if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
{
cmsUInt16Number tmp;
_cmsAssert(io != NULL);
tmp = _cmsAdjustEndianess16(n);
if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
{
cmsUInt32Number i;
_cmsAssert(io != NULL);
_cmsAssert(Array != NULL);
for (i=0; i < n; i++) {
if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
}
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
tmp = _cmsAdjustEndianess32(n);
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
tmp = *(cmsUInt32Number*) &n;
tmp = _cmsAdjustEndianess32(tmp);
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number n)
{
cmsUInt64Number tmp;
_cmsAssert(io != NULL);
_cmsAdjustEndianess64(&tmp, n);
if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
tmp = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(n));
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
{
cmsEncodedXYZNumber xyz;
_cmsAssert(io != NULL);
_cmsAssert(XYZ != NULL);
xyz.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->X));
xyz.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Y));
xyz.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Z));
return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz);
}
// from Fixed point 8.8 to double
cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
{
cmsUInt8Number msb, lsb;
lsb = (cmsUInt8Number) (fixed8 & 0xff);
msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
}
cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
{
cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
}
// from Fixed point 15.16 to double
cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
{
cmsFloat64Number floater, sign, mid;
int Whole, FracPart;
sign = (fix32 < 0 ? -1 : 1);
fix32 = abs(fix32);
Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
FracPart = (cmsUInt16Number)(fix32 & 0xffff);
mid = (cmsFloat64Number) FracPart / 65536.0;
floater = (cmsFloat64Number) Whole + mid;
return sign * floater;
}
// from double to Fixed point 15.16
cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
{
return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
}
// Date/Time functions
void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
{
_cmsAssert(Dest != NULL);
_cmsAssert(Source != NULL);
Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds);
Dest->tm_min = _cmsAdjustEndianess16(Source->minutes);
Dest->tm_hour = _cmsAdjustEndianess16(Source->hours);
Dest->tm_mday = _cmsAdjustEndianess16(Source->day);
Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1;
Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900;
Dest->tm_wday = -1;
Dest->tm_yday = -1;
Dest->tm_isdst = 0;
}
void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
{
_cmsAssert(Dest != NULL);
_cmsAssert(Source != NULL);
Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
}
// Read base and return type base
cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
{
_cmsTagBase Base;
_cmsAssert(io != NULL);
if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
return (cmsTagTypeSignature) 0;
return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
}
// Setup base marker
cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
{
_cmsTagBase Base;
_cmsAssert(io != NULL);
Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
memset(&Base.reserved, 0, sizeof(Base.reserved));
return io -> Write(io, sizeof(_cmsTagBase), &Base);
}
cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
{
cmsUInt8Number Buffer[4];
cmsUInt32Number NextAligned, At;
cmsUInt32Number BytesToNextAlignedPos;
_cmsAssert(io != NULL);
At = io -> Tell(io);
NextAligned = _cmsALIGNLONG(At);
BytesToNextAlignedPos = NextAligned - At;
if (BytesToNextAlignedPos == 0) return TRUE;
if (BytesToNextAlignedPos > 4) return FALSE;
return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
}
cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
{
cmsUInt8Number Buffer[4];
cmsUInt32Number NextAligned, At;
cmsUInt32Number BytesToNextAlignedPos;
_cmsAssert(io != NULL);
At = io -> Tell(io);
NextAligned = _cmsALIGNLONG(At);
BytesToNextAlignedPos = NextAligned - At;
if (BytesToNextAlignedPos == 0) return TRUE;
if (BytesToNextAlignedPos > 4) return FALSE;
memset(Buffer, 0, BytesToNextAlignedPos);
return io -> Write(io, BytesToNextAlignedPos, Buffer);
}
// To deal with text streams. 2K at most
cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
{
va_list args;
int len;
cmsUInt8Number Buffer[2048];
cmsBool rc;
_cmsAssert(io != NULL);
_cmsAssert(frm != NULL);
va_start(args, frm);
len = vsnprintf((char*) Buffer, 2047, frm, args);
if (len < 0) return FALSE; // Truncated, which is a fatal error for us
rc = io ->Write(io, len, Buffer);
va_end(args);
return rc;
}
// Plugin memory management -------------------------------------------------------------------------------------------------
static _cmsSubAllocator* PluginPool = NULL;
// Specialized malloc for plug-ins, that is freed upon exit.
void* _cmsPluginMalloc(cmsUInt32Number size)
{
if (PluginPool == NULL)
PluginPool = _cmsCreateSubAlloc(0, 4*1024);
return _cmsSubAlloc(PluginPool, size);
}
// Main plug-in dispatcher
cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
{
cmsPluginBase* Plugin;
for (Plugin = (cmsPluginBase*) Plug_in;
Plugin != NULL;
Plugin = Plugin -> Next) {
if (Plugin -> Magic != cmsPluginMagicNumber) {
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
return FALSE;
}
if (Plugin ->ExpectedVersion > LCMS_VERSION) {
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
Plugin ->ExpectedVersion, LCMS_VERSION);
return FALSE;
}
switch (Plugin -> Type) {
case cmsPluginMemHandlerSig:
if (!_cmsRegisterMemHandlerPlugin(Plugin)) return FALSE;
break;
case cmsPluginInterpolationSig:
if (!_cmsRegisterInterpPlugin(Plugin)) return FALSE;
break;
case cmsPluginTagTypeSig:
if (!_cmsRegisterTagTypePlugin(Plugin)) return FALSE;
break;
case cmsPluginTagSig:
if (!_cmsRegisterTagPlugin(Plugin)) return FALSE;
break;
case cmsPluginFormattersSig:
if (!_cmsRegisterFormattersPlugin(Plugin)) return FALSE;
break;
case cmsPluginRenderingIntentSig:
if (!_cmsRegisterRenderingIntentPlugin(Plugin)) return FALSE;
break;
case cmsPluginParametricCurveSig:
if (!_cmsRegisterParametricCurvesPlugin(Plugin)) return FALSE;
break;
case cmsPluginMultiProcessElementSig:
if (!_cmsRegisterMultiProcessElementPlugin(Plugin)) return FALSE;
break;
case cmsPluginOptimizationSig:
if (!_cmsRegisterOptimizationPlugin(Plugin)) return FALSE;
break;
default:
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
return FALSE;
}
}
// Keep a reference to the plug-in
return TRUE;
}
// Revert all plug-ins to default
void CMSEXPORT cmsUnregisterPlugins(void)
{
_cmsRegisterMemHandlerPlugin(NULL);
_cmsRegisterInterpPlugin(NULL);
_cmsRegisterTagTypePlugin(NULL);
_cmsRegisterTagPlugin(NULL);
_cmsRegisterFormattersPlugin(NULL);
_cmsRegisterRenderingIntentPlugin(NULL);
_cmsRegisterParametricCurvesPlugin(NULL);
_cmsRegisterMultiProcessElementPlugin(NULL);
_cmsRegisterOptimizationPlugin(NULL);
if (PluginPool != NULL)
_cmsSubAllocDestroy(PluginPool);
PluginPool = NULL;
}

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,10 @@
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
// Little Color Management System
// Copyright (c) 1998-2010 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"),
@ -48,650 +49,246 @@
// 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;
}
#include "lcms2_internal.h"
// pow() restricted to integer
// This file contains routines for resampling and LUT optimization, black point detection
// and black preservation.
// Black point detection -------------------------------------------------------------------------
// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs
static
int ipow(int base, int exp)
cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
{
int res = base;
cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
cmsHTRANSFORM xform;
cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE };
cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 };
cmsHPROFILE hProfiles[4];
cmsUInt32Number Intents[4];
cmsContext ContextID = cmsGetProfileContextID(hProfile);
while (--exp)
res *= base;
hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab;
Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC;
return res;
xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents,
States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
cmsCloseProfile(hLab);
return xform;
}
// Given n, 0<=n<=clut^dim, returns the colorant.
// Use darker colorants to obtain black point. This works in the relative colorimetric intent and
// assumes more ink results in darker colors. No ink limit is assumed.
static
int ComponentOf(int n, int clut, int nColorant)
cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
cmsUInt32Number Intent,
cmsCIEXYZ* BlackPoint,
cmsUInt32Number dwFlags)
{
if (nColorant <= 0)
return (n % clut);
cmsUInt16Number *Black;
cmsHTRANSFORM xform;
cmsColorSpaceSignature Space;
cmsUInt32Number nChannels;
cmsUInt32Number dwFormat;
cmsHPROFILE hLab;
cmsCIELab Lab;
cmsCIEXYZ BlackXYZ;
cmsContext ContextID = cmsGetProfileContextID(hInput);
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);
}
for (t=0; t < (int) Lut -> OutputChan; t++)
Out[t] = Lut->T[index + t];
if (dwFlags & SAMPLER_HASTL2) {
for (t=0; t < (int) Lut -> OutputChan; t++)
Out[t] = cmsLinearInterpLUT16(Out[t],
Lut -> L2[t],
&Lut -> Out16params);
}
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;
// 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 FALSE;
}
// Create a formatter which has n channels and floating point
dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE);
// Try to get black by using black colorant
Space = cmsGetColorSpace(hInput);
// This function returns darker colorant in 16 bits for several spaces
if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
if (nChannels != T_CHANNELS(dwFormat)) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// Lab will be used as the output space, but lab2 will avoid recursion
hLab = cmsCreateLab2ProfileTHR(ContextID, NULL);
if (hLab == NULL) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// Create the transform
xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat,
hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
cmsCloseProfile(hLab);
if (xform == NULL) {
// Something went wrong. Get rid of open resources and return zero as black
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// Convert black to Lab
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;
// Free the resources
cmsDeleteTransform(xform);
// Convert from Lab (which is now clipped) to XYZ.
cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
if (BlackPoint != NULL)
*BlackPoint = BlackXYZ;
return TRUE;
cmsUNUSED_PARAMETER(dwFlags);
}
// Get a black point of output CMYK profile, discounting any ink-limiting embedded
// in the profile. For doing that, we use perceptual intent in input direction:
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
static
cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile)
{
cmsHTRANSFORM hRoundTrip;
cmsCIELab LabIn, LabOut;
cmsCIEXYZ BlackXYZ;
// Is the intent supported by the profile?
if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return TRUE;
}
hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL);
if (hRoundTrip == NULL) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
LabIn.L = LabIn.a = LabIn.b = 0;
cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1);
// Clip Lab to reasonable limits
if (LabOut.L > 50) LabOut.L = 50;
LabOut.a = LabOut.b = 0;
cmsDeleteTransform(hRoundTrip);
// Convert it to XYZ
cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
if (BlackPoint != NULL)
*BlackPoint = BlackXYZ;
return TRUE;
}
// 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.
// 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)
cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
{
WORD LabK[4];
double SumCMY, SumCMYK, Error;
cmsCIELab ColorimetricLab, BlackPreservingLab;
BPCARGO* bp = (LPBPCARGO) Cargo;
// Zero for black point
if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) {
// 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;
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// 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;
// v4 + perceptual & saturation intents does have its own black point, and it is
// well specified enough to use it. Black point tag is deprecated in V4.
if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
(Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
// No, mesure and keep Lab measurement for further usage
cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
// Matrix shaper share MRC & perceptual intents
if (cmsIsMatrixShaper(hProfile))
return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);
// 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);
// Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents
BlackPoint -> X = cmsPERCEPTUAL_BLACK_X;
BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y;
BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z;
// 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 TRUE;
}
return 1;
}
#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG
// Sample whole gamut to estimate maximum TAC
// v2, v4 rel/abs colorimetric
if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) &&
Intent == INTENT_RELATIVE_COLORIMETRIC) {
#ifdef _MSC_VER
#pragma warning(disable : 4100)
cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
cmsCIELab Lab;
// If black point is specified, then use it,
BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag);
if (BlackPtr != NULL) {
BlackXYZ = *BlackPtr;
_cmsReadMediaWhitePoint(&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);
if (BlackPoint != NULL)
*BlackPoint = TrustedBlackPoint;
return TRUE;
}
}
#endif
static
int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
BPCARGO* bp = (LPBPCARGO) Cargo;
WORD RoundTrip[4];
int Sum;
// That is about v2 profiles.
cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
// If output profile, discount ink-limiting and that's all
if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
(cmsGetDeviceClass(hProfile) == cmsSigOutputClass) &&
(cmsGetColorSpace(hProfile) == cmsSigCmykData))
return BlackPointUsingPerceptualBlack(BlackPoint, hProfile);
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;
}
#pragma warning(disable: 4550)
// 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;
// Nope, compute BP using current intent.
return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
}

View File

@ -0,0 +1,762 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 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.
//
//---------------------------------------------------------------------------------
//
#include "lcms2_internal.h"
// ------------------------------------------------------------------------
// Gamut boundary description by using Jan Morovic's Segment maxima method
// Many thanks to Jan for allowing me to use his algorithm.
// r = C*
// alpha = Hab
// theta = L*
#define SECTORS 16 // number of divisions in alpha and theta
// Spherical coordinates
typedef struct {
cmsFloat64Number r;
cmsFloat64Number alpha;
cmsFloat64Number theta;
} cmsSpherical;
typedef enum {
GP_EMPTY,
GP_SPECIFIED,
GP_MODELED
} GDBPointType;
typedef struct {
GDBPointType Type;
cmsSpherical p; // Keep also alpha & theta of maximum
} cmsGDBPoint;
typedef struct {
cmsContext ContextID;
cmsGDBPoint Gamut[SECTORS][SECTORS];
} cmsGDB;
// A line using the parametric form
// P = a + t*u
typedef struct {
cmsVEC3 a;
cmsVEC3 u;
} cmsLine;
// A plane using the parametric form
// Q = b + r*v + s*w
typedef struct {
cmsVEC3 b;
cmsVEC3 v;
cmsVEC3 w;
} cmsPlane;
// --------------------------------------------------------------------------------------------
// ATAN2() which always returns degree positive numbers
static
cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x)
{
cmsFloat64Number a;
// Deal with undefined case
if (x == 0.0 && y == 0.0) return 0;
a = (atan2(y, x) * 180.0) / M_PI;
while (a < 0) {
a += 360;
}
return a;
}
// Convert to spherical coordinates
static
void ToSpherical(cmsSpherical* sp, const cmsVEC3* v)
{
cmsFloat64Number L, a, b;
L = v ->n[VX];
a = v ->n[VY];
b = v ->n[VZ];
sp ->r = sqrt( L*L + a*a + b*b );
if (sp ->r == 0) {
sp ->alpha = sp ->theta = 0;
return;
}
sp ->alpha = _cmsAtan2(a, b);
sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L);
}
// Convert to cartesian from spherical
static
void ToCartesian(cmsVEC3* v, const cmsSpherical* sp)
{
cmsFloat64Number sin_alpha;
cmsFloat64Number cos_alpha;
cmsFloat64Number sin_theta;
cmsFloat64Number cos_theta;
cmsFloat64Number L, a, b;
sin_alpha = sin((M_PI * sp ->alpha) / 180.0);
cos_alpha = cos((M_PI * sp ->alpha) / 180.0);
sin_theta = sin((M_PI * sp ->theta) / 180.0);
cos_theta = cos((M_PI * sp ->theta) / 180.0);
a = sp ->r * sin_theta * sin_alpha;
b = sp ->r * sin_theta * cos_alpha;
L = sp ->r * cos_theta;
v ->n[VX] = L;
v ->n[VY] = a;
v ->n[VZ] = b;
}
// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector
// The limits are the centers of each sector, so
static
void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta)
{
*alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) );
*theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) );
if (*alpha >= SECTORS)
*alpha = SECTORS-1;
if (*theta >= SECTORS)
*theta = SECTORS-1;
}
// Line determined by 2 points
static
void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b)
{
_cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]);
_cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX],
b ->n[VY] - a ->n[VY],
b ->n[VZ] - a ->n[VZ]);
}
// Evaluate parametric line
static
void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t)
{
p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX];
p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY];
p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ];
}
/*
Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1)
http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
Copyright 2001, softSurfer (www.softsurfer.com)
This code may be freely used and modified for any purpose
providing that this copyright notice is included with it.
SoftSurfer makes no warranty for this code, and cannot be held
liable for any real or imagined damage resulting from its use.
Users of this code must verify correctness for their application.
*/
static
cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2)
{
cmsFloat64Number a, b, c, d, e, D;
cmsFloat64Number sc, sN, sD;
cmsFloat64Number tc, tN, tD;
cmsVEC3 w0;
_cmsVEC3minus(&w0, &line1 ->a, &line2 ->a);
a = _cmsVEC3dot(&line1 ->u, &line1 ->u);
b = _cmsVEC3dot(&line1 ->u, &line2 ->u);
c = _cmsVEC3dot(&line2 ->u, &line2 ->u);
d = _cmsVEC3dot(&line1 ->u, &w0);
e = _cmsVEC3dot(&line2 ->u, &w0);
D = a*c - b * b; // Denominator
sD = tD = D; // default sD = D >= 0
if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel
sN = 0.0; // force using point P0 on segment S1
sD = 1.0; // to prevent possible division by 0.0 later
tN = e;
tD = c;
}
else { // get the closest points on the infinite lines
sN = (b*e - c*d);
tN = (a*e - b*d);
if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
sN = 0.0;
tN = e;
tD = c;
}
else if (sN > sD) { // sc > 1 => the s=1 edge is visible
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
tN = 0.0;
// recompute sc for this edge
if (-d < 0.0)
sN = 0.0;
else if (-d > a)
sN = sD;
else {
sN = -d;
sD = a;
}
}
else if (tN > tD) { // tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge
if ((-d + b) < 0.0)
sN = 0;
else if ((-d + b) > a)
sN = sD;
else {
sN = (-d + b);
sD = a;
}
}
// finally do the division to get sc and tc
sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD);
tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD);
GetPointOfLine(r, line1, sc);
return TRUE;
}
// ------------------------------------------------------------------ Wrapper
// Allocate & free structure
cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID)
{
cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB));
if (gbd == NULL) return NULL;
gbd -> ContextID = ContextID;
return (cmsHANDLE) gbd;
}
void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD)
{
cmsGDB* gbd = (cmsGDB*) hGBD;
if (hGBD != NULL)
_cmsFree(gbd->ContextID, (void*) gbd);
}
// Auxiliar to retrieve a pointer to the segmentr containing the Lab value
static
cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp)
{
cmsVEC3 v;
int alpha, theta;
// Housekeeping
_cmsAssert(gbd != NULL);
_cmsAssert(Lab != NULL);
_cmsAssert(sp != NULL);
// Center L* by substracting half of its domain, that's 50
_cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b);
// Convert to spherical coordinates
ToSpherical(sp, &v);
if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) {
cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range");
return NULL;
}
// On which sector it falls?
QuantizeToSector(sp, &alpha, &theta);
if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) {
cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range");
return NULL;
}
// Get pointer to the sector
return &gbd ->Gamut[theta][alpha];
}
// Add a point to gamut descriptor. Point to add is in Lab color space.
// GBD is centered on a=b=0 and L*=50
cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab)
{
cmsGDB* gbd = (cmsGDB*) hGBD;
cmsGDBPoint* ptr;
cmsSpherical sp;
// Get pointer to the sector
ptr = GetPoint(gbd, Lab, &sp);
if (ptr == NULL) return FALSE;
// If no samples at this sector, add it
if (ptr ->Type == GP_EMPTY) {
ptr -> Type = GP_SPECIFIED;
ptr -> p = sp;
}
else {
// Substitute only if radius is greater
if (sp.r > ptr -> p.r) {
ptr -> Type = GP_SPECIFIED;
ptr -> p = sp;
}
}
return TRUE;
}
// Check if a given point falls inside gamut
cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab)
{
cmsGDB* gbd = (cmsGDB*) hGBD;
cmsGDBPoint* ptr;
cmsSpherical sp;
// Get pointer to the sector
ptr = GetPoint(gbd, Lab, &sp);
if (ptr == NULL) return FALSE;
// If no samples at this sector, return no data
if (ptr ->Type == GP_EMPTY) return FALSE;
// In gamut only if radius is greater
return (sp.r <= ptr -> p.r);
}
// -----------------------------------------------------------------------------------------------------------------------
// Find near sectors. The list of sectors found is returned on Close[].
// The function returns the number of sectors as well.
// 24 9 10 11 12
// 23 8 1 2 13
// 22 7 * 3 14
// 21 6 5 4 15
// 20 19 18 17 16
//
// Those are the relative movements
// {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2},
// {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1},
// {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0},
// {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1},
// {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}};
static
const struct _spiral {
int AdvX, AdvY;
} Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1},
{-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2},
{+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2},
{-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} };
#define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral))
static
int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[])
{
int nSectors = 0;
int i, a, t;
cmsGDBPoint* pt;
for (i=0; i < NSTEPS; i++) {
a = alpha + Spiral[i].AdvX;
t = theta + Spiral[i].AdvY;
// Cycle at the end
a %= SECTORS;
t %= SECTORS;
// Cycle at the begin
if (a < 0) a = SECTORS + a;
if (t < 0) t = SECTORS + t;
pt = &gbd ->Gamut[t][a];
if (pt -> Type != GP_EMPTY) {
Close[nSectors++] = pt;
}
}
return nSectors;
}
// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid
static
cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta)
{
cmsSpherical sp;
cmsVEC3 Lab;
cmsVEC3 Centre;
cmsLine ray;
int nCloseSectors;
cmsGDBPoint* Close[NSTEPS];
cmsSpherical closel, templ;
cmsLine edge;
int k, m;
// Is that point already specified?
if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE;
// Fill close points
nCloseSectors = FindNearSectors(gbd, alpha, theta, Close);
// Find a central point on the sector
sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS);
sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS);
sp.r = 50.0;
// Convert to Cartesian
ToCartesian(&Lab, &sp);
// Create a ray line from centre to this point
_cmsVEC3init(&Centre, 50.0, 0, 0);
LineOf2Points(&ray, &Lab, &Centre);
// For all close sectors
closel.r = 0.0;
closel.alpha = 0;
closel.theta = 0;
for (k=0; k < nCloseSectors; k++) {
for(m = k+1; m < nCloseSectors; m++) {
cmsVEC3 temp, a1, a2;
// A line from sector to sector
ToCartesian(&a1, &Close[k]->p);
ToCartesian(&a2, &Close[m]->p);
LineOf2Points(&edge, &a1, &a2);
// Find a line
ClosestLineToLine(&temp, &ray, &edge);
// Convert to spherical
ToSpherical(&templ, &temp);
if ( templ.r > closel.r &&
templ.theta >= (theta*180.0/SECTORS) &&
templ.theta <= ((theta+1)*180.0/SECTORS) &&
templ.alpha >= (alpha*360.0/SECTORS) &&
templ.alpha <= ((alpha+1)*360.0/SECTORS)) {
closel = templ;
}
}
}
gbd ->Gamut[theta][alpha].p = closel;
gbd ->Gamut[theta][alpha].Type = GP_MODELED;
return TRUE;
}
// Interpolate missing parts. The algorithm fist computes slices at
// theta=0 and theta=Max.
cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags)
{
int alpha, theta;
cmsGDB* gbd = (cmsGDB*) hGBD;
_cmsAssert(hGBD != NULL);
// Interpolate black
for (alpha = 0; alpha <= SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE;
}
// Interpolate white
for (alpha = 0; alpha <= SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE;
}
// Interpolate Mid
for (theta = 1; theta < SECTORS; theta++) {
for (alpha = 0; alpha <= SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE;
}
}
// Done
return TRUE;
cmsUNUSED_PARAMETER(dwFlags);
}
// --------------------------------------------------------------------------------------------------------
// Great for debug, but not suitable for real use
#if 0
cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname)
{
FILE* fp;
int i, j;
cmsGDB* gbd = (cmsGDB*) hGBD;
cmsGDBPoint* pt;
fp = fopen (fname, "wt");
if (fp == NULL)
return FALSE;
fprintf (fp, "#VRML V2.0 utf8\n");
// set the viewing orientation and distance
fprintf (fp, "DEF CamTest Group {\n");
fprintf (fp, "\tchildren [\n");
fprintf (fp, "\t\tDEF Cameras Group {\n");
fprintf (fp, "\t\t\tchildren [\n");
fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n");
fprintf (fp, "\t\t\t\t\tposition 0 0 340\n");
fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n");
fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n");
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t]\n");
fprintf (fp, "\t\t},\n");
fprintf (fp, "\t]\n");
fprintf (fp, "}\n");
// Output the background stuff
fprintf (fp, "Background {\n");
fprintf (fp, "\tskyColor [\n");
fprintf (fp, "\t\t.5 .5 .5\n");
fprintf (fp, "\t]\n");
fprintf (fp, "}\n");
// Output the shape stuff
fprintf (fp, "Transform {\n");
fprintf (fp, "\tscale .3 .3 .3\n");
fprintf (fp, "\tchildren [\n");
// Draw the axes as a shape:
fprintf (fp, "\t\tShape {\n");
fprintf (fp, "\t\t\tappearance Appearance {\n");
fprintf (fp, "\t\t\t\tmaterial Material {\n");
fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n");
fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n");
fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
fprintf (fp, "\t\t\t\t\tpoint [\n");
fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n");
fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0);
fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0);
fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0);
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t\tcoordIndex [\n");
fprintf (fp, "\t\t\t\t\t0, 1, -1\n");
fprintf (fp, "\t\t\t\t\t0, 2, -1\n");
fprintf (fp, "\t\t\t\t\t0, 3, -1]\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t}\n");
fprintf (fp, "\t\tShape {\n");
fprintf (fp, "\t\t\tappearance Appearance {\n");
fprintf (fp, "\t\t\t\tmaterial Material {\n");
fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n");
fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n");
fprintf (fp, "\t\t\t\t\tshininess 0.8\n");
fprintf (fp, "\t\t\t\t}\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t\tgeometry PointSet {\n");
// fill in the points here
fprintf (fp, "\t\t\t\tcoord Coordinate {\n");
fprintf (fp, "\t\t\t\t\tpoint [\n");
// We need to transverse all gamut hull.
for (i=0; i < SECTORS; i++)
for (j=0; j < SECTORS; j++) {
cmsVEC3 v;
pt = &gbd ->Gamut[i][j];
ToCartesian(&v, &pt ->p);
fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]);
if ((j == SECTORS - 1) && (i == SECTORS - 1))
fprintf (fp, "]\n");
else
fprintf (fp, ",\n");
}
fprintf (fp, "\t\t\t\t}\n");
// fill in the face colors
fprintf (fp, "\t\t\t\tcolor Color {\n");
fprintf (fp, "\t\t\t\t\tcolor [\n");
for (i=0; i < SECTORS; i++)
for (j=0; j < SECTORS; j++) {
cmsVEC3 v;
pt = &gbd ->Gamut[i][j];
ToCartesian(&v, &pt ->p);
if (pt ->Type == GP_EMPTY)
fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0);
else
if (pt ->Type == GP_MODELED)
fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5);
else {
fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0);
}
if ((j == SECTORS - 1) && (i == SECTORS - 1))
fprintf (fp, "]\n");
else
fprintf (fp, ",\n");
}
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t\t}\n");
fprintf (fp, "\t\t}\n");
fprintf (fp, "\t]\n");
fprintf (fp, "}\n");
fclose (fp);
return TRUE;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,10 @@
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little cms
// Copyright (C) 1998-2007 Marti Maria
// Little Color Management System
// Copyright (c) 1998-2010 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"),
@ -48,43 +49,38 @@
// 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"
#include "lcms2_internal.h"
// Conversions
void LCMSEXPORT cmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* Source)
// D50 - Widely used
const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void)
{
double ISum;
static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z};
ISum = 1./(Source -> X + Source -> Y + Source -> Z);
Dest -> x = (Source -> X) * ISum;
Dest -> y = (Source -> Y) * ISum;
Dest -> Y = Source -> Y;
return &D50XYZ;
}
void LCMSEXPORT cmsxyY2XYZ(LPcmsCIEXYZ Dest, const cmsCIExyY* Source)
const cmsCIExyY* CMSEXPORT cmsD50_xyY(void)
{
static cmsCIExyY D50xyY;
Dest -> X = (Source -> x / Source -> y) * Source -> Y;
Dest -> Y = Source -> Y;
Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y;
cmsXYZ2xyY(&D50xyY, cmsD50_XYZ());
return &D50xyY;
}
// Obtains WhitePoint from Temperature
LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK)
{
double x, y;
double T, T2, T3;
// double M1, M2;
cmsFloat64Number x, y;
cmsFloat64Number T, T2, T3;
// cmsFloat64Number M1, M2;
// No optimization provided.
_cmsAssert(WhitePoint != NULL);
T = TempK;
T2 = T*T; // Square
@ -104,7 +100,7 @@ LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
}
else {
cmsSignalError(LCMS_ERRC_ABORTED, "cmsWhitePointFromTemp: invalid temp");
cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp");
return FALSE;
}
@ -117,10 +113,6 @@ LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
// 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;
@ -128,205 +120,16 @@ LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
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
cmsFloat64Number mirek; // temp (in microreciprocal kelvin)
cmsFloat64Number ut; // u coord of intersection w/ blackbody locus
cmsFloat64Number vt; // v coord of intersection w/ blackbody locus
cmsFloat64Number tt; // slope of ISOTEMPERATURE. line
} ISOTEMPERATURE,FAR* LPISOTEMPERATURE;
} ISOTEMPERATURE;
static ISOTEMPERATURE isotempdata[] = {
// {Mirek, Ut, Vt, Tt }
@ -360,27 +163,28 @@ static ISOTEMPERATURE isotempdata[] = {
{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 }
{600, 0.33724, 0.36051, -116.45 }
};
#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE)
// Robertson's method
static
double Robertson(LPcmsCIExyY v)
cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint)
{
int j;
double us,vs;
double uj,vj,tj,di,dj,mi,mj;
double Tc = -1, xs, ys;
cmsFloat64Number us,vs;
cmsFloat64Number uj,vj,tj,di,dj,mi,mj;
cmsFloat64Number xs, ys;
_cmsAssert(WhitePoint != NULL);
_cmsAssert(TempK != NULL);
di = mi = 0;
xs = v -> x;
ys = v -> y;
xs = WhitePoint -> x;
ys = WhitePoint -> y;
// convert (x,y) to CIE 1960 (u,v)
// convert (x,y) to CIE 1960 (u,WhitePoint)
us = (2*xs) / (-xs + 6*ys + 1.5);
vs = (3*ys) / (-xs + 6*ys + 1.5);
@ -393,332 +197,182 @@ double Robertson(LPcmsCIExyY v)
tj = isotempdata[j].tt;
mj = isotempdata[j].mirek;
dj = ((vs - vj) - tj * (us - uj)) / sqrt(1 + tj*tj);
dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj);
if ((j!=0) && (di/dj < 0.0)) {
Tc = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
break;
if ((j != 0) && (di/dj < 0.0)) {
// Found a match
*TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
return TRUE;
}
di = dj;
mi = mj;
}
if (j == NISO) return -1;
return Tc;
// Not found
return FALSE;
}
// Compute chromatic adaptation matrix using Chad as cone matrix
static
LCMSBOOL InRange(LPcmsCIExyY a, LPcmsCIExyY b, double tolerance)
cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion,
const cmsCIEXYZ* SourceWhitePoint,
const cmsCIEXYZ* DestWhitePoint,
const cmsMAT3* Chad)
{
double dist_x, dist_y;
dist_x = fabs(a->x - b->x);
dist_y = fabs(a->y - b->y);
cmsMAT3 Chad_Inv;
cmsVEC3 ConeSourceXYZ, ConeSourceRGB;
cmsVEC3 ConeDestXYZ, ConeDestRGB;
cmsMAT3 Cone, Tmp;
return (tolerance >= dist_x * dist_x + dist_y * dist_y);
Tmp = *Chad;
if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) return FALSE;
_cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X,
SourceWhitePoint -> Y,
SourceWhitePoint -> Z);
_cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X,
DestWhitePoint -> Y,
DestWhitePoint -> Z);
_cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ);
_cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ);
// Build matrix
_cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0);
_cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0);
_cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]);
// Normalize
_cmsMAT3per(&Tmp, &Cone, Chad);
_cmsMAT3per(Conversion, &Chad_Inv, &Tmp);
return TRUE;
}
// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed
cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll)
{
cmsMAT3 LamRigg = {{ // Bradford matrix
{{ 0.8951, 0.2664, -0.1614 }},
{{ -0.7502, 1.7135, 0.0367 }},
{{ 0.0389, -0.0685, 1.0296 }}
}};
typedef struct {
char Name[30];
cmsCIExyY Val;
if (ConeMatrix == NULL)
ConeMatrix = &LamRigg;
} WHITEPOINTS,FAR *LPWHITEPOINTS;
return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
}
// Same as anterior, but assuming D50 destination. White point is given in xyY
static
int FromD40toD150(LPWHITEPOINTS pts)
cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt)
{
int i, n;
cmsCIEXYZ Dn;
cmsMAT3 Bradford;
cmsMAT3 Tmp;
n = 0;
for (i=40; i < 150; i ++)
{
sprintf(pts[n].Name, "D%d", i);
cmsWhitePointFromTemp((int) (i*100.0), &pts[n].Val);
n++;
}
cmsxyY2XYZ(&Dn, SourceWhitePt);
return n;
if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE;
Tmp = *r;
_cmsMAT3per(r, &Bradford, &Tmp);
return TRUE;
}
// To be removed in future versions
void _cmsIdentifyWhitePoint(char *Buffer, LPcmsCIEXYZ WhitePt)
// 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
//
cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs)
{
int i, n;
cmsCIExyY Val;
double T;
WHITEPOINTS SomeIlluminants[140] = {
cmsVEC3 WhitePoint, Coef;
cmsMAT3 Result, Primaries;
cmsFloat64Number xn, yn;
cmsFloat64Number xr, yr;
cmsFloat64Number xg, yg;
cmsFloat64Number xb, yb;
{"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}},
};
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;
n = FromD40toD150(&SomeIlluminants[3]) + 3;
// Build Primaries matrix
_cmsVEC3init(&Primaries.v[0], xr, xg, xb);
_cmsVEC3init(&Primaries.v[1], yr, yg, yb);
_cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb));
cmsXYZ2xyY(&Val, WhitePt);
Val.Y = 1.;
for (i=0; i < n; i++)
{
// Result = Primaries ^ (-1) inverse matrix
if (!_cmsMAT3inverse(&Primaries, &Result))
return FALSE;
if (InRange(&Val, &SomeIlluminants[i].Val, 0.000005))
{
strcpy(Buffer, "WhitePoint : ");
strcat(Buffer, SomeIlluminants[i].Name);
return;
}
}
T = Robertson(&Val);
_cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
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);
// Across inverse primaries ...
_cmsMAT3eval(&Coef, &Result, &WhitePoint);
}
// Give us the Coefs, then I build transformation matrix
_cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb);
_cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb);
_cmsVEC3init(&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 _cmsAdaptMatrixToD50(r, WhitePt);
}
// Use darker colorant to obtain black point
static
int BlackPointAsDarkerColorant(cmsHPROFILE hInput,
int Intent,
LPcmsCIEXYZ BlackPoint,
DWORD dwFlags)
// Adapts a color to a given illuminant. Original color is expected to have
// a SourceWhitePt white point.
cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result,
const cmsCIEXYZ* SourceWhitePt,
const cmsCIEXYZ* Illuminant,
const cmsCIEXYZ* Value)
{
WORD *Black, *White;
cmsHTRANSFORM xform;
icColorSpaceSignature Space;
int nChannels;
DWORD dwFormat;
cmsHPROFILE hLab;
cmsCIELab Lab;
cmsCIEXYZ BlackXYZ, MediaWhite;
cmsMAT3 Bradford;
cmsVEC3 In, Out;
// If the profile does not support input direction, assume Black point 0
if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) {
_cmsAssert(Result != NULL);
_cmsAssert(SourceWhitePt != NULL);
_cmsAssert(Illuminant != NULL);
_cmsAssert(Value != NULL);
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return 0;
}
if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE;
_cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z);
_cmsMAT3eval(&Out, &Bradford, &In);
// 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);
Result -> X = Out.n[0];
Result -> Y = Out.n[1];
Result -> Z = Out.n[2];
return TRUE;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,678 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 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.
//
//---------------------------------------------------------------------------------
//
#ifndef _lcms_internal_H
// Include plug-in foundation
#ifndef _lcms_plugin_H
# include "lcms2_plugin.h"
#endif
// ctype is part of C99 as per 7.1.2
#include <ctype.h>
// assert macro is part of C99 as per 7.2
#include <assert.h>
// Some needed constants
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#ifndef M_LOG10E
# define M_LOG10E 0.434294481903251827651
#endif
// BorlandC 5.5 is broken on that
#ifdef __BORLANDC__
#define sinf(x) (float)sin((float)x)
#define sqrtf(x) (float)sqrt((float)x)
#endif
// Alignment of ICC file format uses 4 bytes (cmsUInt32Number)
#define _cmsSIZEOFLONGMINUS1 (sizeof(cmsUInt32Number)-1)
#define _cmsALIGNLONG(x) (((x)+_cmsSIZEOFLONGMINUS1) & ~(_cmsSIZEOFLONGMINUS1))
// Maximum encodeable values in floating point
#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0)
#define MIN_ENCODEABLE_ab2 (-128.0)
#define MAX_ENCODEABLE_ab2 ((65535.0/256.0) - 128.0)
#define MIN_ENCODEABLE_ab4 (-128.0)
#define MAX_ENCODEABLE_ab4 (127.0)
// Maximum of channels for internal pipeline evaluation
#define MAX_STAGE_CHANNELS 128
// Unused parameter warning supression
#define cmsUNUSED_PARAMETER(x) ((void)x)
// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999).
// unfortunately VisualC++ does not conform that
#if defined(_MSC_VER) || defined(__BORLANDC__)
# define cmsINLINE __inline
#else
# define cmsINLINE static inline
#endif
// Other replacement functions
#ifdef _MSC_VER
# ifndef snprintf
# define snprintf _snprintf
# endif
# ifndef vsnprintf
# define vsnprintf _vsnprintf
# endif
#endif
// Pthreads. In windows we use the native WIN32 API instead
#ifdef CMS_DONT_USE_PTHREADS
typedef int LCMS_RWLOCK_T;
# define LCMS_CREATE_LOCK(x)
# define LCMS_FREE_LOCK(x)
# define LCMS_READ_LOCK(x)
# define LCMS_WRITE_LOCK(x)
# define LCMS_UNLOCK(x)
#else
#ifdef CMS_IS_WINDOWS_
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
typedef CRITICAL_SECTION LCMS_RWLOCK_T;
# define LCMS_CREATE_LOCK(x) InitializeCriticalSection((x))
# define LCMS_FREE_LOCK(x) DeleteCriticalSection((x))
# define LCMS_READ_LOCK(x) EnterCriticalSection((x))
# define LCMS_WRITE_LOCK(x) EnterCriticalSection((x))
# define LCMS_UNLOCK(x) LeaveCriticalSection((x))
#else
# include <pthread.h>
typedef pthread_rwlock_t LCMS_RWLOCK_T;
# define LCMS_CREATE_LOCK(x) pthread_rwlock_init((x), NULL)
# define LCMS_FREE_LOCK(x) pthread_rwlock_destroy((x))
# define LCMS_READ_LOCK(x) pthread_rwlock_rdlock((x))
# define LCMS_WRITE_LOCK(x) pthread_rwlock_wrlock((x))
# define LCMS_UNLOCK(x) pthread_rwlock_unlock((x))
#endif
#endif
// A fast way to convert from/to 16 <-> 8 bits
#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb))
#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF)
// Code analysis is broken on asserts
#ifdef _MSC_VER
# if (_MSC_VER >= 1500)
# define _cmsAssert(a) { assert((a)); __analysis_assume((a)); }
# else
# define _cmsAssert(a) assert((a))
# endif
#else
# define _cmsAssert(a) assert((a))
#endif
//---------------------------------------------------------------------------------
// Determinant lower than that are assumed zero (used on matrix invert)
#define MATRIX_DET_TOLERANCE 0.0001
//---------------------------------------------------------------------------------
// Fixed point
#define FIXED_TO_INT(x) ((x)>>16)
#define FIXED_REST_TO_INT(x) ((x)&0xFFFFU)
#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16)
cmsINLINE cmsS15Fixed16Number _cmsToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); }
cmsINLINE int _cmsFromFixedDomain(cmsS15Fixed16Number a) { return a - ((a + 0x7fff) >> 16); }
// -----------------------------------------------------------------------------------------------------------
// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon
// note than this only works in the range ..-32767...+32767 because
// mantissa is interpreted as 15.16 fixed point.
// The union is to avoid pointer aliasing overoptimization.
cmsINLINE int _cmsQuickFloor(cmsFloat64Number val)
{
#ifdef CMS_DONT_USE_FAST_FLOOR
return (int) floor(val);
#else
const cmsFloat64Number _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor
union {
cmsFloat64Number val;
int halves[2];
} temp;
temp.val = val + _lcms_double2fixmagic;
#ifdef CMS_USE_BIG_ENDIAN
return temp.halves[1] >> 16;
#else
return temp.halves[0] >> 16;
#endif
#endif
}
// Fast floor restricted to 0..65535.0
cmsINLINE cmsUInt16Number _cmsQuickFloorWord(cmsFloat64Number d)
{
return (cmsUInt16Number) _cmsQuickFloor(d - 32767.0) + 32767U;
}
// Floor to word, taking care of saturation
cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d)
{
d += 0.5;
if (d <= 0) return 0;
if (d >= 65535.0) return 0xffff;
return _cmsQuickFloorWord(d);
}
// Plug-In registering ---------------------------------------------------------------
// Specialized function for plug-in memory management. No pairing free() since whole pool is freed at once.
void* _cmsPluginMalloc(cmsUInt32Number size);
// Memory management
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin);
// Interpolation
cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Plugin);
// Parametric curves
cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Plugin);
// Formatters management
cmsBool _cmsRegisterFormattersPlugin(cmsPluginBase* Plugin);
// Tag type management
cmsBool _cmsRegisterTagTypePlugin(cmsPluginBase* Plugin);
// Tag management
cmsBool _cmsRegisterTagPlugin(cmsPluginBase* Plugin);
// Intent management
cmsBool _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Plugin);
// Multi Process elements
cmsBool _cmsRegisterMultiProcessElementPlugin(cmsPluginBase* Plugin);
// Optimization
cmsBool _cmsRegisterOptimizationPlugin(cmsPluginBase* Plugin);
// ---------------------------------------------------------------------------------------------------------
// Suballocators. Those are blocks of memory that is freed at the end on whole block.
typedef struct _cmsSubAllocator_chunk_st {
cmsUInt8Number* Block;
cmsUInt32Number BlockSize;
cmsUInt32Number Used;
struct _cmsSubAllocator_chunk_st* next;
} _cmsSubAllocator_chunk;
typedef struct {
cmsContext ContextID;
_cmsSubAllocator_chunk* h;
} _cmsSubAllocator;
_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial);
void _cmsSubAllocDestroy(_cmsSubAllocator* s);
void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size);
// ----------------------------------------------------------------------------------
// MLU internal representation
typedef struct {
cmsUInt16Number Language;
cmsUInt16Number Country;
cmsUInt32Number StrW; // Offset to current unicode string
cmsUInt32Number Len; // Lenght in bytes
} _cmsMLUentry;
struct _cms_MLU_struct {
cmsContext ContextID;
// The directory
int AllocatedEntries;
int UsedEntries;
_cmsMLUentry* Entries; // Array of pointers to strings allocated in MemPool
// The Pool
cmsUInt32Number PoolSize; // The maximum allocated size
cmsUInt32Number PoolUsed; // The used size
void* MemPool; // Pointer to begin of memory pool
};
// Named color list internal representation
typedef struct {
char Name[cmsMAX_PATH];
cmsUInt16Number PCS[3];
cmsUInt16Number DeviceColorant[cmsMAXCHANNELS];
} _cmsNAMEDCOLOR;
struct _cms_NAMEDCOLORLIST_struct {
cmsUInt32Number nColors;
cmsUInt32Number Allocated;
cmsUInt32Number ColorantCount;
char Prefix[33]; // Prefix and suffix are defined to be 32 characters at most
char Suffix[33];
_cmsNAMEDCOLOR* List;
cmsContext ContextID;
};
// ----------------------------------------------------------------------------------
// This is the internal struct holding profile details.
// Maximum supported tags in a profile
#define MAX_TABLE_TAG 100
typedef struct _cms_iccprofile_struct {
// I/O handler
cmsIOHANDLER* IOhandler;
// The thread ID
cmsContext ContextID;
// Creation time
struct tm Created;
// Only most important items found in ICC profiles
cmsUInt32Number Version;
cmsProfileClassSignature DeviceClass;
cmsColorSpaceSignature ColorSpace;
cmsColorSpaceSignature PCS;
cmsUInt32Number RenderingIntent;
cmsUInt32Number flags;
cmsUInt32Number manufacturer, model;
cmsUInt64Number attributes;
cmsProfileID ProfileID;
// Dictionary
cmsUInt32Number TagCount;
cmsTagSignature TagNames[MAX_TABLE_TAG];
cmsTagSignature TagLinked[MAX_TABLE_TAG]; // The tag to wich is linked (0=none)
cmsUInt32Number TagSizes[MAX_TABLE_TAG]; // Size on disk
cmsUInt32Number TagOffsets[MAX_TABLE_TAG];
cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked
void * TagPtrs[MAX_TABLE_TAG];
cmsTagTypeHandler* TagTypeHandlers[MAX_TABLE_TAG]; // Same structure may be serialized on different types
// depending on profile version, so we keep track of the // type handler for each tag in the list.
// Special
cmsBool IsWrite;
} _cmsICCPROFILE;
// IO helpers for profiles
cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc);
cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace);
int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks);
// Tag types
cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsTagTypeSignature sig);
cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig);
cmsTagDescriptor* _cmsGetTagDescriptor(cmsTagSignature sig);
// Error logging ---------------------------------------------------------------------------------------------------------
void _cmsTagSignature2String(char String[5], cmsTagSignature sig);
// Interpolation ---------------------------------------------------------------------------------------------------------
cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
void _cmsFreeInterpParams(cmsInterpParams* p);
// Curves ----------------------------------------------------------------------------------------------------------------
// This struct holds information about a segment, plus a pointer to the function that implements the evaluation.
// In the case of table-based, Eval pointer is set to NULL
// The gamma function main structure
struct _cms_curve_struct {
cmsInterpParams* InterpParams; // Private optimizations for interpolation
cmsUInt32Number nSegments; // Number of segments in the curve. Zero for a 16-bit based tables
cmsCurveSegment* Segments; // The segments
cmsInterpParams** SegInterp; // Array of private optimizations for interpolation in table-based segments
cmsParametricCurveEvaluator* Evals; // Evaluators (one per segment)
// 16 bit Table-based representation follows
cmsUInt32Number nEntries; // Number of table elements
cmsUInt16Number* Table16; // The table itself.
};
// Pipelines & Stages ---------------------------------------------------------------------------------------------
// A single stage
struct _cmsStage_struct {
cmsContext ContextID;
cmsStageSignature Type; // Identifies the stage
cmsStageSignature Implements; // Identifies the *function* of the stage (for optimizations)
cmsUInt32Number InputChannels; // Input channels -- for optimization purposes
cmsUInt32Number OutputChannels; // Output channels -- for optimization purposes
_cmsStageEvalFn EvalPtr; // Points to fn that evaluates the stage (always in floating point)
_cmsStageDupElemFn DupElemPtr; // Points to a fn that duplicates the *data* of the stage
_cmsStageFreeElemFn FreePtr; // Points to a fn that sets the *data* of the stage free
// A generic pointer to whatever memory needed by the stage
void* Data;
// Maintains linked list (used internally)
struct _cmsStage_struct* Next;
};
// Data kept in "Element" member of cmsStage
// Curves
typedef struct {
cmsUInt32Number nCurves;
cmsToneCurve** TheCurves;
} _cmsStageToneCurvesData;
// Matrix
typedef struct {
cmsFloat64Number* Double; // floating point for the matrix
cmsFloat64Number* Offset; // The offset
} _cmsStageMatrixData;
// CLUT
typedef struct {
union { // Can have only one of both representations at same time
cmsUInt16Number* T; // Points to the table 16 bits table
cmsFloat32Number* TFloat; // Points to the cmsFloat32Number table
} Tab;
cmsInterpParams* Params;
cmsUInt32Number nEntries;
cmsBool HasFloatValues;
} _cmsStageCLutData;
// Special Stages (cannot be saved)
cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID);
cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID);
cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID);
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList);
cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels);
cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan);
// For curve set only
cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe);
// Pipeline Evaluator (in floating point)
typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[],
cmsFloat32Number Out[],
const void* Data);
struct _cmsPipeline_struct {
cmsStage* Elements; // Points to elements chain
cmsUInt32Number InputChannels, OutputChannels;
// Data & evaluators
void *Data;
_cmsOPTeval16Fn Eval16Fn;
_cmsPipelineEvalFloatFn EvalFloatFn;
_cmsOPTfreeDataFn FreeDataFn;
_cmsOPTdupDataFn DupDataFn;
cmsContext ContextID; // Environment
cmsBool SaveAs8Bits; // Implemntation-specific: save as 8 bits if possible
};
// LUT reading & creation -------------------------------------------------------------------------------------------
// Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy
// of the LUTS, since ownership of original is up to the profile. The user should free allocated resources.
cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent);
cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent);
cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent);
// Special values
cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile);
cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile);
// Profile linker --------------------------------------------------------------------------------------------------
cmsPipeline* _cmsLinkProfiles(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number TheIntents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
// Sequence --------------------------------------------------------------------------------------------------------
cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile);
cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq);
cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]);
// LUT optimization ------------------------------------------------------------------------------------------------
cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples);
int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags);
cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
cmsUInt16Number **White,
cmsUInt16Number **Black,
cmsUInt32Number *nOutputs);
cmsBool _cmsOptimizePipeline(cmsPipeline** Lut,
int Intent,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags );
// Hi level LUT building ----------------------------------------------------------------------------------------------
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsUInt32Number Intents[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number nGamutPCSposition,
cmsHPROFILE hGamut);
// Formatters ------------------------------------------------------------------------------------------------------------
cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type);
cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type);
cmsFormatter _cmsGetFormatter(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8
cmsFormatterDirection Dir,
cmsUInt32Number dwFlags);
// Transform logic ------------------------------------------------------------------------------------------------------
struct _cmstransform_struct;
// Full xform
typedef void (* _cmsTransformFn)(struct _cmstransform_struct *Transform,
const void* InputBuffer,
void* OutputBuffer, cmsUInt32Number Size);
typedef struct {
cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference
cmsUInt32Number StrideIn, StrideOut; // Planar support
} cmsFormatterInfo;
// Transformation
typedef struct _cmstransform_struct {
cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference
// Points to transform code
_cmsTransformFn xform;
// Formatters, cannot be embedded into LUT because cache
cmsFormatter16 FromInput;
cmsFormatter16 ToOutput;
cmsFormatterFloat FromInputFloat;
cmsFormatterFloat ToOutputFloat;
// 1-pixel cache (16 bits only)
cmsUInt16Number CacheIn[cmsMAXCHANNELS];
cmsUInt16Number CacheOut[cmsMAXCHANNELS];
// Semaphor for cache
LCMS_RWLOCK_T rwlock;
// A MPE LUT holding the full (optimized) transform
cmsPipeline* Lut;
// A MPE LUT holding the gamut check. It goes from the input space to bilevel
cmsPipeline* GamutCheck;
// Colorant tables
cmsNAMEDCOLORLIST* InputColorant; // Input Colorant table
cmsNAMEDCOLORLIST* OutputColorant; // Colorant table (for n chans > CMYK)
// Informational only
cmsColorSpaceSignature EntryColorSpace;
cmsColorSpaceSignature ExitColorSpace;
// Profiles used to create the transform
cmsSEQ* Sequence;
cmsUInt32Number dwOriginalFlags;
cmsFloat64Number AdaptationState;
// The intent of this transform. That is usually the last intent in the profilechain, but may differ
cmsUInt32Number RenderingIntent;
// An id that uniquely identifies the running context. May be null.
cmsContext ContextID;
} _cmsTRANSFORM;
// --------------------------------------------------------------------------------------------------
cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number InputFormat,
cmsUInt32Number OutputFormat,
const cmsUInt32Number Intents[],
const cmsHPROFILE hProfiles[],
const cmsBool BPC[],
const cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
cmsUInt32Number nPoints,
cmsUInt32Number nProfiles,
const cmsUInt32Number Intents[],
const cmsHPROFILE hProfiles[],
const cmsBool BPC[],
const cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll);
cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries);
#define _lcms_internal_H
#endif

View File

@ -0,0 +1,562 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 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.
//
//---------------------------------------------------------------------------------
//
// This is the plug-in header file. Normal LittleCMS clients should not use it.
// It is provided for plug-in writters that may want to access the support
// functions to do low level operations. All plug-in related structures
// are defined here. Including this file forces to include the standard API too.
#ifndef _lcms_plugin_H
// Deal with Microsoft's attempt at deprecating C standard runtime functions
#ifdef _MSC_VER
# if (_MSC_VER >= 1400)
# ifndef _CRT_SECURE_NO_DEPRECATE
# define _CRT_SECURE_NO_DEPRECATE
# endif
# ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
# endif
# endif
#endif
#ifndef _lcms2_H
#include "lcms2.h"
#endif
// We need some standard C functions.
#include <stdlib.h>
#include <math.h>
#include <stdarg.h>
#include <memory.h>
#include <string.h>
#ifndef CMS_USE_CPP_API
# ifdef __cplusplus
extern "C" {
# endif
#endif
// Vector & Matrix operations -----------------------------------------------------------------------
// Axis of the matrix/array. No specific meaning at all.
#define VX 0
#define VY 1
#define VZ 2
// Vectors
typedef struct {
cmsFloat64Number n[3];
} cmsVEC3;
// 3x3 Matrix
typedef struct {
cmsVEC3 v[3];
} cmsMAT3;
CMSAPI void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z);
CMSAPI void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b);
CMSAPI void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v);
CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v);
CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a);
CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b);
CMSAPI void CMSEXPORT _cmsMAT3identity(cmsMAT3* a);
CMSAPI cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a);
CMSAPI void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b);
CMSAPI cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b);
CMSAPI cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b);
CMSAPI void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v);
// Error logging -------------------------------------------------------------------------------------
CMSAPI void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...);
// Memory management ----------------------------------------------------------------------------------
CMSAPI void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size);
CMSAPI void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size);
CMSAPI void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size);
CMSAPI void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize);
CMSAPI void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr);
CMSAPI void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size);
// I/O handler ----------------------------------------------------------------------------------
struct _cms_io_handler {
void* stream; // Associated stream, which is implemented differently depending on media.
cmsContext ContextID;
cmsUInt32Number UsedSpace;
char PhysicalFile[cmsMAX_PATH];
cmsUInt32Number (* Read)(struct _cms_io_handler* iohandler, void *Buffer,
cmsUInt32Number size,
cmsUInt32Number count);
cmsBool (* Seek)(struct _cms_io_handler* iohandler, cmsUInt32Number offset);
cmsBool (* Close)(struct _cms_io_handler* iohandler);
cmsUInt32Number (* Tell)(struct _cms_io_handler* iohandler);
cmsBool (* Write)(struct _cms_io_handler* iohandler, cmsUInt32Number size,
const void* Buffer);
};
// Endianess adjust functions
CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word);
CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value);
CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number QWord);
// Helper IO functions
CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n);
CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n);
CMSAPI cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n);
CMSAPI cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n);
CMSAPI cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n);
CMSAPI cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n);
CMSAPI cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ);
CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number n);
CMSAPI cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array);
// ICC base tag
typedef struct {
cmsTagTypeSignature sig;
cmsInt8Number reserved[4];
} _cmsTagBase;
// Type base helper functions
CMSAPI cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io);
CMSAPI cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig);
// Alignment functions
CMSAPI cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io);
CMSAPI cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io);
// To deal with text streams. 2K at most
CMSAPI cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...);
// Fixed point helper functions
CMSAPI cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8);
CMSAPI cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val);
CMSAPI cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32);
CMSAPI cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v);
// Date/time helper functions
CMSAPI void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source);
CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest);
//----------------------------------------------------------------------------------------------------------
// Plug-in foundation
#define cmsPluginMagicNumber 0x61637070 // 'acpp'
#define cmsPluginMemHandlerSig 0x6D656D48 // 'memH'
#define cmsPluginInterpolationSig 0x696E7048 // 'inpH'
#define cmsPluginParametricCurveSig 0x70617248 // 'parH'
#define cmsPluginFormattersSig 0x66726D48 // 'frmH
#define cmsPluginTagTypeSig 0x74797048 // 'typH'
#define cmsPluginTagSig 0x74616748 // 'tagH'
#define cmsPluginRenderingIntentSig 0x696E7448 // 'intH'
#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH'
#define cmsPluginOptimizationSig 0x6F707448 // 'optH'
typedef struct _cmsPluginBaseStruct {
cmsUInt32Number Magic; // 'acpp' signature
cmsUInt32Number ExpectedVersion; // Expected version of LittleCMS
cmsUInt32Number Type; // Type of plug-in
struct _cmsPluginBaseStruct* Next; // For multiple plugin definition. NULL for end of list.
} cmsPluginBase;
// Maximum number of types in a plugin array
#define MAX_TYPES_IN_LCMS_PLUGIN 20
//----------------------------------------------------------------------------------------------------------
// Memory handler. Each new plug-in type replaces current behaviour
typedef struct {
cmsPluginBase base;
// Required
void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size);
void (* FreePtr)(cmsContext ContextID, void *Ptr);
void * (* ReallocPtr)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize);
// Optional
void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size);
void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size);
void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size);
} cmsPluginMemHandler;
// ------------------------------------------------------------------------------------------------------------------
// Interpolation. 16 bits and floating point versions.
struct _cms_interp_struc;
// Interpolation callbacks
// 16 bits forward interpolation. This function performs precision-limited linear interpolation
// and is supposed to be quite fast. Implementation may be tetrahedral or trilinear, and plug-ins may
// choose to implement any other interpolation algorithm.
typedef void (* _cmsInterpFn16)(register const cmsUInt16Number Input[],
register cmsUInt16Number Output[],
register const struct _cms_interp_struc* p);
// Floating point forward interpolation. Full precision interpolation using floats. This is not a
// time critical function. Implementation may be tetrahedral or trilinear, and plug-ins may
// choose to implement any other interpolation algorithm.
typedef void (* _cmsInterpFnFloat)(cmsFloat32Number const Input[],
cmsFloat32Number Output[],
const struct _cms_interp_struc* p);
// This type holds a pointer to an interpolator that can be either 16 bits or float
typedef union {
_cmsInterpFn16 Lerp16; // Forward interpolation in 16 bits
_cmsInterpFnFloat LerpFloat; // Forward interpolation in floating point
} cmsInterpFunction;
// Flags for interpolator selection
#define CMS_LERP_FLAGS_16BITS 0x0000 // The default
#define CMS_LERP_FLAGS_FLOAT 0x0001 // Requires different implementation
#define CMS_LERP_FLAGS_TRILINEAR 0x0100 // Hint only
#define MAX_INPUT_DIMENSIONS 8
typedef struct _cms_interp_struc { // Used on all interpolations. Supplied by lcms2 when calling the interpolation function
cmsContext ContextID; // The calling thread
cmsUInt32Number dwFlags; // Keep original flags
cmsUInt32Number nInputs; // != 1 only in 3D interpolation
cmsUInt32Number nOutputs; // != 1 only in 3D interpolation
cmsUInt32Number nSamples[MAX_INPUT_DIMENSIONS]; // Valid on all kinds of tables
cmsUInt32Number Domain[MAX_INPUT_DIMENSIONS]; // Domain = nSamples - 1
cmsUInt32Number opta[MAX_INPUT_DIMENSIONS]; // Optimization for 3D CLUT. This is the number of nodes premultiplied for each
// dimension. For example, in 7 nodes, 7, 7^2 , 7^3, 7^4, etc. On non-regular
// Samplings may vary according of the number of nodes for each dimension.
const void *Table; // Points to the actual interpolation table
cmsInterpFunction Interpolation; // Points to the function to do the interpolation
} cmsInterpParams;
// Interpolators factory
typedef cmsInterpFunction (* cmsInterpFnFactory)(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags);
// The plug-in
typedef struct {
cmsPluginBase base;
// Points to a user-supplied function which implements the factory
cmsInterpFnFactory InterpolatorsFactory;
} cmsPluginInterpolation;
//----------------------------------------------------------------------------------------------------------
// Parametric curves. A negative type means same function but analytically inverted. Max. number of params is 10
// Evaluator callback for user-suplied parametric curves. May implement more than one type
typedef cmsFloat64Number (* cmsParametricCurveEvaluator)(cmsInt32Number Type, const cmsFloat64Number Params[10], cmsFloat64Number R);
// Plug-in may implement an arbitrary number of parametric curves
typedef struct {
cmsPluginBase base;
cmsUInt32Number nFunctions; // Number of supported functions
cmsUInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types
cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function
cmsParametricCurveEvaluator Evaluator; // The evaluator
} cmsPluginParametricCurves;
//----------------------------------------------------------------------------------------------------------
// Formatters. This plug-in adds new handlers, replacing them if they already exist. Formatters dealing with
// cmsFloat32Number (bps = 4) or double (bps = 0) types are requested via FormatterFloat callback. Others come across
// Formatter16 callback
struct _cmstransform_struct;
typedef cmsUInt8Number* (* cmsFormatter16)(register struct _cmstransform_struct* CMMcargo,
register cmsUInt16Number Values[],
register cmsUInt8Number* Buffer,
register cmsUInt32Number Stride);
typedef cmsUInt8Number* (* cmsFormatterFloat)(struct _cmstransform_struct* CMMcargo,
cmsFloat32Number Values[],
cmsUInt8Number* Buffer,
cmsUInt32Number Stride);
// This type holds a pointer to a formatter that can be either 16 bits or cmsFloat32Number
typedef union {
cmsFormatter16 Fmt16;
cmsFormatterFloat FmtFloat;
} cmsFormatter;
#define CMS_PACK_FLAGS_16BITS 0x0000
#define CMS_PACK_FLAGS_FLOAT 0x0001
typedef enum { cmsFormatterInput=0, cmsFormatterOutput=1 } cmsFormatterDirection;
typedef cmsFormatter (* cmsFormatterFactory)(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8
cmsFormatterDirection Dir,
cmsUInt32Number dwFlags); // precision
// Plug-in may implement an arbitrary number of formatters
typedef struct {
cmsPluginBase base;
cmsFormatterFactory FormattersFactory;
} cmsPluginFormatters;
//----------------------------------------------------------------------------------------------------------
// Tag type handler. Each type is free to return anything it wants, and it is up to the caller to
// know in advance what is the type contained in the tag.
typedef struct _cms_typehandler_struct {
cmsTagTypeSignature Signature; // The signature of the type
// Allocates and reads items
void * (* ReadPtr)(struct _cms_typehandler_struct* self,
cmsIOHANDLER* io,
cmsUInt32Number* nItems,
cmsUInt32Number SizeOfTag);
// Writes n Items
cmsBool (* WritePtr)(struct _cms_typehandler_struct* self,
cmsIOHANDLER* io,
void* Ptr,
cmsUInt32Number nItems);
// Duplicate an item or array of items
void* (* DupPtr)(struct _cms_typehandler_struct* self,
const void *Ptr,
cmsUInt32Number n);
// Free all resources
void (* FreePtr)(struct _cms_typehandler_struct* self,
void *Ptr);
// The calling thread
cmsContext ContextID;
} cmsTagTypeHandler;
// Each plug-in implements a single type
typedef struct {
cmsPluginBase base;
cmsTagTypeHandler Handler;
} cmsPluginTagType;
//----------------------------------------------------------------------------------------------------------
// This is the tag plugin, which identifies tags. For writing, a pointer to function is provided.
// This function should return the desired type for this tag, given the version of profile
// and the data being serialized.
typedef struct {
cmsUInt32Number ElemCount; // If this tag needs an array, how many elements should keep
// For reading.
cmsUInt32Number nSupportedTypes; // In how many types this tag can come (MAX_TYPES_IN_LCMS_PLUGIN maximum)
cmsTagTypeSignature SupportedTypes[MAX_TYPES_IN_LCMS_PLUGIN];
// For writting
cmsTagTypeSignature (* DecideType)(cmsFloat64Number ICCVersion, const void *Data);
} cmsTagDescriptor;
// Plug-in implements a single tag
typedef struct {
cmsPluginBase base;
cmsTagSignature Signature;
cmsTagDescriptor Descriptor;
} cmsPluginTag;
//----------------------------------------------------------------------------------------------------------
// Custom intents. This function should join all profiles specified in the array in
// a single LUT. Any custom intent in the chain redirects to custom function. If more than
// one custom intent is found, the one located first is invoked. Usually users should use only one
// custom intent, so mixing custom intents in same multiprofile transform is not supported.
typedef cmsPipeline* (* cmsIntentFn)( cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number Intents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
// Each plug-in defines a single intent number.
typedef struct {
cmsPluginBase base;
cmsUInt32Number Intent;
cmsIntentFn Link;
char Description[256];
} cmsPluginRenderingIntent;
// The default ICC intents (perceptual, saturation, rel.col and abs.col)
CMSAPI cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID,
cmsUInt32Number nProfiles,
cmsUInt32Number Intents[],
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags);
//----------------------------------------------------------------------------------------------------------
// Pipelines, Multi Process Elements.
typedef void (* _cmsStageEvalFn) (const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage* mpe);
typedef void*(* _cmsStageDupElemFn) (cmsStage* mpe);
typedef void (* _cmsStageFreeElemFn) (cmsStage* mpe);
// This function allocates a generic MPE
CMSAPI cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID,
cmsStageSignature Type,
cmsUInt32Number InputChannels,
cmsUInt32Number OutputChannels,
_cmsStageEvalFn EvalPtr, // Points to fn that evaluates the element (always in floating point)
_cmsStageDupElemFn DupElemPtr, // Points to a fn that duplicates the stage
_cmsStageFreeElemFn FreePtr, // Points to a fn that sets the element free
void* Data); // A generic pointer to whatever memory needed by the element
typedef struct {
cmsPluginBase base;
cmsTagTypeHandler Handler;
} cmsPluginMultiProcessElement;
//----------------------------------------------------------------------------------------------------------
// Optimization. Using this plug-in, additional optimization strategies may be implemented.
// The function should return TRUE if any optimization is done on the LUT, this terminates
// the optimization search. Or FALSE if it is unable to optimize and want to give a chance
// to the rest of optimizers.
typedef void (* _cmsOPTeval16Fn)(register const cmsUInt16Number In[],
register cmsUInt16Number Out[],
register const void* Data);
typedef void (* _cmsOPTfreeDataFn)(cmsContext ContextID, void* Data);
typedef void* (* _cmsOPTdupDataFn)(cmsContext ContextID, const void* Data);
typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut,
cmsUInt32Number Intent,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags);
// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional
// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality.
CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
_cmsOPTeval16Fn Eval16,
void* PrivateData,
_cmsOPTfreeDataFn FreePrivateDataFn,
_cmsOPTdupDataFn DupPrivateDataFn);
typedef struct {
cmsPluginBase base;
// Optimize entry point
_cmsOPToptimizeFn OptimizePtr;
} cmsPluginOptimization;
//----------------------------------------------------------------------------------------------------------
#ifndef CMS_USE_CPP_API
# ifdef __cplusplus
}
# endif
#endif
#define _lcms_plugin_H
#endif