mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-17 05:45:05 +00:00
6523398: OSS CMM: Need to implement writing ICC profile tags in new lcms library
Reviewed-by: igor, prr
This commit is contained in:
parent
2d2d444adf
commit
899dcb6de0
@ -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 \
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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 )));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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];
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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
@ -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
@ -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);
|
||||
}
|
||||
346
jdk/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c
Normal file
346
jdk/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c
Normal 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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
1657
jdk/src/share/native/sun/java2d/cmm/lcms/cmsopt.c
Normal file
1657
jdk/src/share/native/sun/java2d/cmm/lcms/cmsopt.c
Normal file
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
641
jdk/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c
Normal file
641
jdk/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c
Normal 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
@ -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);
|
||||
}
|
||||
|
||||
|
||||
762
jdk/src/share/native/sun/java2d/cmm/lcms/cmssm.c
Normal file
762
jdk/src/share/native/sun/java2d/cmm/lcms/cmssm.c
Normal 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
|
||||
4863
jdk/src/share/native/sun/java2d/cmm/lcms/cmstypes.c
Normal file
4863
jdk/src/share/native/sun/java2d/cmm/lcms/cmstypes.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
1751
jdk/src/share/native/sun/java2d/cmm/lcms/lcms2.h
Normal file
1751
jdk/src/share/native/sun/java2d/cmm/lcms/lcms2.h
Normal file
File diff suppressed because it is too large
Load Diff
678
jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h
Normal file
678
jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h
Normal 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
|
||||
562
jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h
Normal file
562
jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user