Switch to libcms2-2.6

This commit is contained in:
mayeut 2015-07-21 23:49:11 +02:00
parent 28c6f54798
commit 2fc9d4956a
29 changed files with 11407 additions and 6249 deletions

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2014 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"),
@ -23,7 +23,7 @@
//
//---------------------------------------------------------------------------------
//
// Version 2.1
// Version 2.6
//
#ifndef _lcms2_H
@ -40,9 +40,6 @@
// Uncomment this if your compiler doesn't work with fast floor function
// #define CMS_DONT_USE_FAST_FLOOR 1
// Uncomment this line if your system does not support multithreading
#define CMS_DONT_USE_PTHREADS 1
// Uncomment this line if you want lcms to use the black point tag in profile,
// if commented, lcms will compute the black point by its own.
// It is safer to leave it commented out
@ -55,6 +52,12 @@
// require "KEYWORD" on undefined identifiers, keep it comented out unless needed
// #define CMS_STRICT_CGATS 1
// Uncomment to get rid of the tables for "half" float support
// #define CMS_NO_HALF_SUPPORT 1
// Uncomment to get rid of pthreads/windows dependency
// #define CMS_NO_PTHREADS 1
// ********** End of configuration toggles ******************************
// Needed for streams
@ -72,7 +75,7 @@ extern "C" {
#endif
// Version/release
#define LCMS_VERSION 2010
#define LCMS_VERSION 2060
// I will give the chance of redefining basic types for compilers that are not fully C99 compliant
#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@ -81,6 +84,10 @@ extern "C" {
typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec
typedef signed char cmsInt8Number; // That is guaranteed by the C99 spec
#if CHAR_BIT != 8
# error "Unable to find 8 bit type, unsupported compiler"
#endif
// IEEE float storage numbers
typedef float cmsFloat32Number;
typedef double cmsFloat64Number;
@ -169,26 +176,42 @@ typedef int cmsBool;
// Try to detect big endian platforms. This list can be endless, so only some checks are performed over here.
// you can pass this toggle to the compiler by using -DCMS_USE_BIG_ENDIAN or something similar
#if defined(__sgi__) || defined(__sgi) || defined(sparc)
# define CMS_USE_BIG_ENDIAN 1
#endif
#if defined(__s390__) || defined(__s390x__)
# define CMS_USE_BIG_ENDIAN 1
#endif
# ifdef TARGET_CPU_PPC
# if TARGET_CPU_PPC
# define CMS_USE_BIG_ENDIAN 1
# endif
# endif
#if defined(__powerpc__) || defined(__ppc__) || defined(TARGET_CPU_PPC)
# define CMS_USE_BIG_ENDIAN 1
# if defined (__GNUC__) && defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN)
# if __BYTE_ORDER == __LITTLE_ENDIAN
// // Don't use big endian for PowerPC little endian mode
# undef CMS_USE_BIG_ENDIAN
# endif
# endif
#endif
// WORDS_BIGENDIAN takes precedence
#if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(WORDS_BIGENDIAN)
# define CMS_USE_BIG_ENDIAN 1
#endif
#if defined(__sgi__) || defined(__sgi) || defined(__powerpc__) || defined(sparc)
# define CMS_USE_BIG_ENDIAN 1
#endif
#if defined(__ppc__) || defined(__s390__) || defined(__s390x__)
# define CMS_USE_BIG_ENDIAN 1
#endif
#if TARGET_CPU_PPC
# define CMS_USE_BIG_ENDIAN 1
#endif
#ifdef macintosh
# ifdef __BIG_ENDIAN__
# define CMS_USE_BIG_ENDIAN 1
# endif
# ifdef __LITTLE_ENDIAN__
# undef CMS_USE_BIG_ENDIAN
# endif
#endif
// Calling convention -- this is hardly platform and compiler dependent
@ -214,6 +237,14 @@ typedef int cmsBool;
# define CMSAPI
#endif
#ifdef HasTHREADS
# if HasTHREADS == 1
# undef CMS_NO_PTHREADS
# else
# define CMS_NO_PTHREADS 1
# endif
#endif
// Some common definitions
#define cmsMAX_PATH 256
@ -247,6 +278,7 @@ typedef enum {
cmsSigCrdInfoType = 0x63726469, // 'crdi'
cmsSigCurveType = 0x63757276, // 'curv'
cmsSigDataType = 0x64617461, // 'data'
cmsSigDictType = 0x64696374, // 'dict'
cmsSigDateTimeType = 0x6474696D, // 'dtim'
cmsSigDeviceSettingsType = 0x64657673, // 'devs'
cmsSigLut16Type = 0x6d667432, // 'mft2'
@ -273,9 +305,10 @@ typedef enum {
cmsSigUInt32ArrayType = 0x75693332, // 'ui32'
cmsSigUInt64ArrayType = 0x75693634, // 'ui64'
cmsSigUInt8ArrayType = 0x75693038, // 'ui08'
cmsSigVcgtType = 0x76636774, // 'vcgt'
cmsSigViewingConditionsType = 0x76696577, // 'view'
cmsSigXYZType = 0x58595A20, // 'XYZ '
cmsSigVcgtType = 0x76636774 // 'vcgt'
cmsSigXYZType = 0x58595A20 // 'XYZ '
} cmsTagTypeSignature;
@ -330,6 +363,7 @@ typedef enum {
cmsSigPreview1Tag = 0x70726531, // 'pre1'
cmsSigPreview2Tag = 0x70726532, // 'pre2'
cmsSigProfileDescriptionTag = 0x64657363, // 'desc'
cmsSigProfileDescriptionMLTag = 0x6473636d, // 'dscm'
cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq'
cmsSigProfileSequenceIdTag = 0x70736964, // 'psid'
cmsSigPs2CRD0Tag = 0x70736430, // 'psd0'
@ -348,7 +382,8 @@ typedef enum {
cmsSigUcrBgTag = 0x62666420, // 'bfd '
cmsSigViewingCondDescTag = 0x76756564, // 'vued'
cmsSigViewingConditionsTag = 0x76696577, // 'view'
cmsSigVcgtTag = 0x76636774 // 'vcgt'
cmsSigVcgtTag = 0x76636774, // 'vcgt'
cmsSigMetaTag = 0x6D657461 // 'meta'
} cmsTagSignature;
@ -407,12 +442,12 @@ typedef enum {
cmsSigMCH7Data = 0x4D434837, // 'MCH7'
cmsSigMCH8Data = 0x4D434838, // 'MCH8'
cmsSigMCH9Data = 0x4D434839, // 'MCH9'
cmsSigMCHAData = 0x4D43483A, // 'MCHA'
cmsSigMCHBData = 0x4D43483B, // 'MCHB'
cmsSigMCHCData = 0x4D43483C, // 'MCHC'
cmsSigMCHDData = 0x4D43483D, // 'MCHD'
cmsSigMCHEData = 0x4D43483E, // 'MCHE'
cmsSigMCHFData = 0x4D43483F, // 'MCHF'
cmsSigMCHAData = 0x4D434841, // 'MCHA'
cmsSigMCHBData = 0x4D434842, // 'MCHB'
cmsSigMCHCData = 0x4D434843, // 'MCHC'
cmsSigMCHDData = 0x4D434844, // 'MCHD'
cmsSigMCHEData = 0x4D434845, // 'MCHE'
cmsSigMCHFData = 0x4D434846, // 'MCHF'
cmsSigNamedData = 0x6e6d636c, // 'nmcl'
cmsSig1colorData = 0x31434C52, // '1CLR'
cmsSig2colorData = 0x32434C52, // '2CLR'
@ -483,7 +518,13 @@ typedef enum {
cmsSigLabV4toV2 = 0x34203220, // '4 2 '
// Identities
cmsSigIdentityElemType = 0x69646E20 // 'idn '
cmsSigIdentityElemType = 0x69646E20, // 'idn '
// Float to floatPCS
cmsSigLab2FloatPCS = 0x64326C20, // 'd2l '
cmsSigFloatPCS2Lab = 0x6C326420, // 'l2d '
cmsSigXYZ2FloatPCS = 0x64327820, // 'd2x '
cmsSigFloatPCS2XYZ = 0x78326420 // 'x2d '
} cmsStageSignature;
@ -597,7 +638,6 @@ typedef struct {
// Little CMS specific typedefs
typedef void* cmsContext; // Context identifier for multithreaded environments
typedef void* cmsHANDLE ; // Generic handle
typedef void* cmsHPROFILE; // Opaque typedefs to hide internals
typedef void* cmsHTRANSFORM;
@ -606,7 +646,9 @@ typedef void* cmsHTRANSFORM;
// Format of pixel is defined by one cmsUInt32Number, using bit fields as follows
//
// A O TTTTT U Y F P X S EEE CCCC BBB
// 2 1 0
// 3 2 10987 6 5 4 3 2 1 098 7654 321
// A O TTTTT U Y F P X S EEE CCCC BBB
//
// A: Floating point -- With this flag we can differentiate 16 bits as float and as int
// O: Optimized -- previous optimization already returns the final 8-bit value
@ -714,16 +756,19 @@ typedef void* cmsHTRANSFORM;
#define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1))
#define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1))
#define TYPE_ARGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1))
#define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1))
#define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1))
#define TYPE_ABGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1))
#define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1))
#define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1))
#define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1))
#define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
#define TYPE_BGRA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1))
#define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1))
#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
#define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1))
#define TYPE_CMY_8_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1))
@ -805,8 +850,8 @@ typedef void* cmsHTRANSFORM;
#define TYPE_Lab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1))
#define TYPE_LabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1))
#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1))
#define TYPE_ALabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1))
#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1))
#define TYPE_ALabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1))
#define TYPE_Lab_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2))
#define TYPE_LabV2_16 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(2))
#define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2))
@ -844,12 +889,17 @@ typedef void* cmsHTRANSFORM;
// Float formatters.
#define TYPE_XYZ_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(4))
#define TYPE_XYZA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
#define TYPE_Lab_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4))
#define TYPE_LabA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
#define TYPE_GRAY_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4))
#define TYPE_RGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4))
#define TYPE_RGBA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
#define TYPE_ARGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1))
#define TYPE_BGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1))
#define TYPE_BGRA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
#define TYPE_ABGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1))
#define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4))
// Floating point formatters.
@ -858,8 +908,21 @@ typedef void* cmsHTRANSFORM;
#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0))
#define TYPE_GRAY_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(0))
#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0))
#define TYPE_BGR_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)|DOSWAP_SH(1))
#define TYPE_CMYK_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0))
// IEEE 754-2008 "half"
#define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2))
#define TYPE_RGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2))
#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2))
#define TYPE_CMYK_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2))
#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2))
#define TYPE_ARGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1))
#define TYPE_BGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1))
#define TYPE_BGRA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
#define TYPE_ABGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1))
#endif
// Colorspaces
@ -944,10 +1007,25 @@ typedef struct {
CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2);
CMSAPI long int CMSEXPORT cmsfilelength(FILE* f);
// Plug-In registering ---------------------------------------------------------------------------------------------------
// Context handling --------------------------------------------------------------------------------------------------------
// Each context holds its owns globals and its own plug-ins. There is a global context with the id = 0 for lecacy compatibility
// though using the global context is not recomended. Proper context handling makes lcms more thread-safe.
typedef struct _cmsContext_struct* cmsContext;
CMSAPI cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData);
CMSAPI void CMSEXPORT cmsDeleteContext(cmsContext ContexID);
CMSAPI cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData);
CMSAPI void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID);
// Plug-In registering --------------------------------------------------------------------------------------------------
CMSAPI cmsBool CMSEXPORT cmsPlugin(void* Plugin);
CMSAPI cmsBool CMSEXPORT cmsPluginTHR(cmsContext ContextID, void* Plugin);
CMSAPI void CMSEXPORT cmsUnregisterPlugins(void);
CMSAPI void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID);
// Error logging ----------------------------------------------------------------------------------------------------------
@ -984,6 +1062,7 @@ typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Numb
// Allows user to set any specific logger
CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn);
CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn);
// Conversions --------------------------------------------------------------------------------------------------------------
@ -1090,6 +1169,10 @@ CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve*
CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t);
CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision);
// Tone curve tabular estimation
CMSAPI cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t);
CMSAPI const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t);
// Implements pipelines of multi-processing elements -------------------------------------------------------------
@ -1102,6 +1185,7 @@ CMSAPI cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUIn
CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut);
CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig);
CMSAPI cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut);
CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut);
CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut);
@ -1118,7 +1202,7 @@ CMSAPI cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lu
// Where to place/locate the stages in the pipeline chain
typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc;
CMSAPI void CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe);
CMSAPI int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe);
CMSAPI void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe);
// This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements
@ -1162,10 +1246,9 @@ typedef cmsInt32Number (* cmsSAMPLERFLOAT)(register const cmsFloat32Number In[],
#define SAMPLER_INSPECT 0x01000000
// For CLUT only
CMSAPI cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags);
CMSAPI cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags);
CMSAPI cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags);
// Slicers
CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
cmsSAMPLER16 Sampler, void * Cargo);
@ -1203,6 +1286,13 @@ CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
char ObtainedLanguage[3], char ObtainedCountry[3]);
CMSAPI cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu);
CMSAPI cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu,
cmsUInt32Number idx,
char LanguageCode[3],
char CountryCode[3]);
// Undercolorremoval & black generation -------------------------------------------------------------------------------------
typedef struct {
@ -1275,6 +1365,7 @@ CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform);
// Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others
// come from Profile Sequence Identifier Tag
typedef struct {
cmsSignature deviceMfg;
cmsSignature deviceModel;
cmsUInt64Number attributes;
@ -1298,6 +1389,27 @@ CMSAPI cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext
CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq);
CMSAPI void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq);
// Dictionaries --------------------------------------------------------------------------------------------------------
typedef struct _cmsDICTentry_struct {
struct _cmsDICTentry_struct* Next;
cmsMLU *DisplayName;
cmsMLU *DisplayValue;
wchar_t* Name;
wchar_t* Value;
} cmsDICTentry;
CMSAPI cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID);
CMSAPI void CMSEXPORT cmsDictFree(cmsHANDLE hDict);
CMSAPI cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict);
CMSAPI cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue);
CMSAPI const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict);
CMSAPI const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e);
// Access to Profile data ----------------------------------------------------------------------------------------------
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID);
@ -1331,6 +1443,7 @@ CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProf
CMSAPI void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile);
CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile);
CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model);
CMSAPI void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags);
@ -1411,6 +1524,7 @@ CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext Context
CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void * MemPtr, cmsUInt32Number dwSize);
CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void * MemPtr, cmsUInt32Number dwSize);
CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io);
CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write);
CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile);
CMSAPI cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName);
@ -1501,6 +1615,7 @@ CMSAPI cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransfo
// Call with NULL as parameters to get the intent count
CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions);
// Flags
@ -1605,15 +1720,37 @@ CMSAPI void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
void * OutputBuffer,
cmsUInt32Number Size);
CMSAPI void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]);
CMSAPI void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
const void * InputBuffer,
void * OutputBuffer,
cmsUInt32Number Size,
cmsUInt32Number Stride);
CMSAPI void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]);
CMSAPI void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]);
CMSAPI void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID,
const cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]);
CMSAPI void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID,
cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]);
// Adaptation state for absolute colorimetric intent
CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d);
CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d);
// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
CMSAPI cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform);
// Grab the input/output formats
CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform);
// For backwards compatibility
CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
cmsUInt32Number InputFormat,
@ -1663,12 +1800,15 @@ CMSAPI cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* c
CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* cProp, const char *Str);
CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val);
CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val);
CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer);
CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer);
CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp);
CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp);
CMSAPI const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey);
CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames);
CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames);
// Datasets
CMSAPI const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col);
@ -1698,10 +1838,13 @@ CMSAPI cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE hIT8, int n, con
CMSAPI int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames);
CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer);
CMSAPI int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch);
// The LABEL extension
CMSAPI int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType);
CMSAPI cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample);
// Formatter for double
CMSAPI void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter);
@ -1717,6 +1860,7 @@ CMSAPI cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIEL
// Estimate the black point
CMSAPI cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags);
CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags);
// Estimate total area coverage
CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile);

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2011 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"),
@ -131,7 +131,7 @@ struct _cms_io_handler {
// Endianess adjust functions
CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word);
CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value);
CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number QWord);
CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord);
// Helper IO functions
CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n);
@ -147,7 +147,7 @@ CMSAPI cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUI
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 _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);
@ -181,6 +181,11 @@ CMSAPI cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v);
CMSAPI void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source);
CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest);
//----------------------------------------------------------------------------------------------------------
// Shared callbacks for user data
typedef void (* _cmsFreeUserDataFn)(cmsContext ContextID, void* Data);
typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data);
//----------------------------------------------------------------------------------------------------------
@ -196,6 +201,8 @@ CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeN
#define cmsPluginRenderingIntentSig 0x696E7448 // 'intH'
#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH'
#define cmsPluginOptimizationSig 0x6F707448 // 'optH'
#define cmsPluginTransformSig 0x7A666D48 // 'xfmH'
#define cmsPluginMutexSig 0x6D747A48 // 'mtxH'
typedef struct _cmsPluginBaseStruct {
@ -212,19 +219,28 @@ typedef struct _cmsPluginBaseStruct {
//----------------------------------------------------------------------------------------------------------
// Memory handler. Each new plug-in type replaces current behaviour
typedef void* (* _cmsMallocFnPtrType)(cmsContext ContextID, cmsUInt32Number size);
typedef void (* _cmsFreeFnPtrType)(cmsContext ContextID, void *Ptr);
typedef void* (* _cmsReallocFnPtrType)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize);
typedef void* (* _cmsMalloZerocFnPtrType)(cmsContext ContextID, cmsUInt32Number size);
typedef void* (* _cmsCallocFnPtrType)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size);
typedef void* (* _cmsDupFnPtrType)(cmsContext ContextID, const void* Org, cmsUInt32Number size);
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);
_cmsMallocFnPtrType MallocPtr;
_cmsFreeFnPtrType FreePtr;
_cmsReallocFnPtrType ReallocPtr;
// Optional
void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size);
void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size);
void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size);
_cmsMalloZerocFnPtrType MallocZeroPtr;
_cmsCallocFnPtrType CallocPtr;
_cmsDupFnPtrType DupPtr;
} cmsPluginMemHandler;
@ -387,7 +403,7 @@ typedef struct _cms_typehandler_struct {
void *Ptr);
// Additional parameters used by the calling thread
cmsContext ContextID;
cmsContext ContextID;
cmsUInt32Number ICCVersion;
} cmsTagTypeHandler;
@ -486,6 +502,39 @@ typedef struct {
} cmsPluginMultiProcessElement;
// 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;
//----------------------------------------------------------------------------------------------------------
// 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
@ -496,9 +545,6 @@ 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,
@ -512,8 +558,8 @@ typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut,
CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
_cmsOPTeval16Fn Eval16,
void* PrivateData,
_cmsOPTfreeDataFn FreePrivateDataFn,
_cmsOPTdupDataFn DupPrivateDataFn);
_cmsFreeUserDataFn FreePrivateDataFn,
_cmsDupUserDataFn DupPrivateDataFn);
typedef struct {
cmsPluginBase base;
@ -524,6 +570,62 @@ typedef struct {
} cmsPluginOptimization;
//----------------------------------------------------------------------------------------------------------
// Full xform
typedef void (* _cmsTransformFn)(struct _cmstransform_struct *CMMcargo,
const void* InputBuffer,
void* OutputBuffer,
cmsUInt32Number Size,
cmsUInt32Number Stride);
typedef cmsBool (* _cmsTransformFactory)(_cmsTransformFn* xform,
void** UserData,
_cmsFreeUserDataFn* FreePrivateDataFn,
cmsPipeline** Lut,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags);
// Retrieve user data as specified by the factory
CMSAPI void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn);
CMSAPI void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo);
// Retrieve formatters
CMSAPI void CMSEXPORT _cmsGetTransformFormatters16 (struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput);
CMSAPI void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput);
typedef struct {
cmsPluginBase base;
// Transform entry point
_cmsTransformFactory Factory;
} cmsPluginTransform;
//----------------------------------------------------------------------------------------------------------
// Mutex
typedef void* (* _cmsCreateMutexFnPtrType)(cmsContext ContextID);
typedef void (* _cmsDestroyMutexFnPtrType)(cmsContext ContextID, void* mtx);
typedef cmsBool (* _cmsLockMutexFnPtrType)(cmsContext ContextID, void* mtx);
typedef void (* _cmsUnlockMutexFnPtrType)(cmsContext ContextID, void* mtx);
typedef struct {
cmsPluginBase base;
_cmsCreateMutexFnPtrType CreateMutexPtr;
_cmsDestroyMutexFnPtrType DestroyMutexPtr;
_cmsLockMutexFnPtrType LockMutexPtr;
_cmsUnlockMutexFnPtrType UnlockMutexPtr;
} cmsPluginMutex;
CMSAPI void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID);
CMSAPI void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx);
CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx);
CMSAPI void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx);
#ifndef CMS_USE_CPP_API
# ifdef __cplusplus

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -52,7 +52,7 @@ typedef struct {
cmsUInt32Number surround;
cmsFloat64Number n, Nbb, Ncb, z, FL, D;
cmsContext ContextID;
cmsContext ContextID;
} cmsCIECAM02;
@ -358,70 +358,70 @@ CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC)
{
cmsCIECAM02* lpMod;
cmsCIECAM02* lpMod;
_cmsAssert(pVC != NULL);
_cmsAssert(pVC != NULL);
if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {
return NULL;
}
if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {
return NULL;
}
lpMod ->ContextID = ContextID;
lpMod ->ContextID = ContextID;
lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
lpMod -> LA = pVC ->La;
lpMod -> Yb = pVC ->Yb;
lpMod -> D = pVC ->D_value;
lpMod -> surround = pVC ->surround;
lpMod -> LA = pVC ->La;
lpMod -> Yb = pVC ->Yb;
lpMod -> D = pVC ->D_value;
lpMod -> surround = pVC ->surround;
switch (lpMod -> surround) {
switch (lpMod -> surround) {
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);
lpMod -> z = compute_z(lpMod);
lpMod -> Nbb = computeNbb(lpMod);
lpMod -> FL = computeFL(lpMod);
lpMod -> n = compute_n(lpMod);
lpMod -> z = compute_z(lpMod);
lpMod -> Nbb = computeNbb(lpMod);
lpMod -> FL = computeFL(lpMod);
if (lpMod -> D == D_CALCULATE) {
lpMod -> D = computeD(lpMod);
}
if (lpMod -> D == D_CALCULATE) {
lpMod -> D = computeD(lpMod);
}
lpMod -> Ncb = lpMod -> Nbb;
lpMod -> Ncb = lpMod -> Nbb;
lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
return (cmsHANDLE) lpMod;
return (cmsHANDLE) lpMod;
}
@ -429,7 +429,7 @@ void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel)
{
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
if (lpMod) _cmsFree(lpMod ->ContextID, lpMod);
if (lpMod) _cmsFree(lpMod ->ContextID, lpMod);
}
@ -438,9 +438,11 @@ void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh
CAM02COLOR clr;
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
_cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL);
_cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL);
memset(&clr, 0, sizeof(clr));
clr.XYZ[0] = pIn ->X;
clr.XYZ[1] = pIn ->Y;
@ -462,9 +464,11 @@ void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ
CAM02COLOR clr;
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
_cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL);
_cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL);
memset(&clr, 0, sizeof(clr));
clr.J = pIn -> J;
clr.C = pIn -> C;
@ -480,4 +484,3 @@ void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ
pOut ->Y = clr.XYZ[1];
pOut ->Z = clr.XYZ[2];
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -30,8 +30,8 @@
// IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
#define MAXID 128 // Max lenght of identifier
#define MAXSTR 1024 // Max lenght of string
#define MAXID 128 // Max length of identifier
#define MAXSTR 1024 // Max length of string
#define MAXTABLES 255 // Max Number of tables in a single stream
#define MAXINCLUDE 20 // Max number of nested includes
@ -44,6 +44,7 @@
# define DIR_CHAR '/'
#endif
// Symbols
typedef enum {
@ -114,6 +115,8 @@ typedef struct _SubAllocator {
// Table. Each individual table can hold properties and rows & cols
typedef struct _Table {
char SheetType[MAXSTR]; // The first row of the IT8 (the type)
int nSamples, nPatches; // Cols, Rows
int SampleID; // Pos of ID
@ -133,7 +136,6 @@ typedef struct _FileContext {
// This struct hold all information about an open IT8 handler.
typedef struct {
char SheetType[MAXSTR]; // The first row of the IT8 (the type)
cmsUInt32Number TablesCount; // How many tables in this stream
cmsUInt32Number nTable; // The actual table
@ -352,28 +354,28 @@ static const char* PredefinedSampleID[] = {
//Forward declaration of some internal functions
static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
// Checks if c is a separator
// Checks whatever c is a separator
static
cmsBool isseparator(int c)
{
return (c == ' ') || (c == '\t') || (c == '\r');
return (c == ' ') || (c == '\t') ;
}
// Checks whatever if c is a valid identifier char
// Checks whatever c is a valid identifier char
static
cmsBool ismiddle(int c)
{
return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
}
// Checks whatsever if c is a valid identifier middle char.
// Checks whatsever c is a valid identifier middle char.
static
cmsBool isidchar(int c)
{
return isalnum(c) || ismiddle(c);
}
// Checks whatsever if c is a valid identifier first char.
// Checks whatsever c is a valid identifier first char.
static
cmsBool isfirstidchar(int c)
{
@ -404,6 +406,7 @@ cmsBool isabsolutepath(const char *path)
return FALSE;
}
// Makes a file path based on a given reference path
// NOTE: this function doesn't check if the path exists or even if it's legal
static
@ -545,7 +548,7 @@ void ReadReal(cmsIT8* it8, int inum)
if (it8->ch == '.') { // Decimal point
cmsFloat64Number frac = 0.0; // fraction
int prec = 0; // precision
int prec = 0; // precision
NextCh(it8); // Eats dec. point
@ -592,6 +595,84 @@ void ReadReal(cmsIT8* it8, int inum)
}
}
// Parses a float number
// This can not call directly atof because it uses locale dependant
// parsing, while CCMX files always use . as decimal separator
static
cmsFloat64Number ParseFloatNumber(const char *Buffer)
{
cmsFloat64Number dnum = 0.0;
int sign = 1;
// keep safe
if (Buffer == NULL) return 0.0;
if (*Buffer == '-' || *Buffer == '+') {
sign = (*Buffer == '-') ? -1 : 1;
Buffer++;
}
while (*Buffer && isdigit((int) *Buffer)) {
dnum = dnum * 10.0 + (*Buffer - '0');
if (*Buffer) Buffer++;
}
if (*Buffer == '.') {
cmsFloat64Number frac = 0.0; // fraction
int prec = 0; // precission
if (*Buffer) Buffer++;
while (*Buffer && isdigit((int) *Buffer)) {
frac = frac * 10.0 + (*Buffer - '0');
prec++;
if (*Buffer) Buffer++;
}
dnum = dnum + (frac / xpow10(prec));
}
// Exponent, example 34.00E+20
if (*Buffer && toupper(*Buffer) == 'E') {
int e;
int sgn;
if (*Buffer) Buffer++;
sgn = 1;
if (*Buffer == '-') {
sgn = -1;
if (*Buffer) Buffer++;
}
else
if (*Buffer == '+') {
sgn = +1;
if (*Buffer) Buffer++;
}
e = 0;
while (*Buffer && isdigit((int) *Buffer)) {
if ((cmsFloat64Number) e * 10L < INT_MAX)
e = e * 10 + (*Buffer - '0');
if (*Buffer) Buffer++;
}
e = sgn*e;
dnum = dnum * xpow10(e);
}
return sign * dnum;
}
// Reads next symbol
@ -759,6 +840,14 @@ void InSymbol(cmsIT8* it8)
// Next line
case '\r':
NextCh(it8);
if (it8 ->ch == '\n')
NextCh(it8);
it8->sy = SEOLN;
it8->lineno++;
break;
case '\n':
NextCh(it8);
it8->sy = SEOLN;
@ -768,7 +857,7 @@ void InSymbol(cmsIT8* it8)
// Comment
case '#':
NextCh(it8);
while (it8->ch && it8->ch != '\n')
while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
NextCh(it8);
it8->sy = SCOMMENT;
@ -886,6 +975,9 @@ cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* Error
{
switch (it8->sy) {
case SEOLN: // Empty value
Buffer[0]=0;
break;
case SIDENT: strncpy(Buffer, it8->id, max);
Buffer[max-1]=0;
break;
@ -982,7 +1074,7 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
cmsUInt8Number* ptr;
size = _cmsALIGNLONG(size);
size = _cmsALIGNMEM(size);
if (size > Free) {
@ -1035,9 +1127,9 @@ cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYV
if (*Key != '#') { // Comments are ignored
if (cmsstrcasecmp(Key, p->Keyword) == 0)
break;
}
break;
}
}
if (p == NULL)
return FALSE;
@ -1047,11 +1139,13 @@ cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYV
for (; p != NULL; p = p->NextSubkey) {
if (p ->Subkey == NULL) continue;
if (LastPtr) *LastPtr = p;
if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
return TRUE;
}
return TRUE;
}
return FALSE;
}
@ -1174,7 +1268,7 @@ cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
it8 ->nTable = nTable;
return nTable;
return (cmsInt32Number) nTable;
}
@ -1183,7 +1277,7 @@ cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
{
cmsIT8* it8;
int i;
cmsUInt32Number i;
it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
if (it8 == NULL) return NULL;
@ -1214,7 +1308,7 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
it8 -> lineno = 1;
strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
strcpy(it8->SheetType, "CGATS.17");
cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
// Initialize predefined properties & data
@ -1231,18 +1325,15 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
return it8 ->SheetType;
return GetTable((cmsIT8*) hIT8)->SheetType;
}
cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t = GetTable((cmsIT8*) hIT8);
strncpy(it8 ->SheetType, Type, MAXSTR-1);
it8 ->SheetType[MAXSTR-1] = 0;
strncpy(t ->SheetType, Type, MAXSTR-1);
t ->SheetType[MAXSTR-1] = 0;
return TRUE;
}
@ -1256,8 +1347,6 @@ cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
}
// Sets a property
cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
{
@ -1269,7 +1358,6 @@ cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const ch
return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
}
cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
@ -1285,7 +1373,7 @@ cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUIn
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buffer[1024];
sprintf(Buffer, "%d", Val);
sprintf(Buffer, "%u", Val);
return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
}
@ -1322,8 +1410,9 @@ cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cPro
{
const char *v = cmsIT8GetProperty(hIT8, cProp);
if (v) return atof(v);
else return 0.0;
if (v == NULL) return 0.0;
return ParseFloatNumber(v);
}
const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
@ -1355,7 +1444,7 @@ void AllocateDataFormat(cmsIT8* it8)
t -> nSamples = 10;
}
t -> DataFormat = (char**) AllocChunk (it8, (t->nSamples + 1) * sizeof(char *));
t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));
if (t->DataFormat == NULL) {
SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
@ -1411,7 +1500,7 @@ void AllocateDataSet(cmsIT8* it8)
t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
t-> Data = (char**)AllocChunk (it8, (t->nSamples + 1) * (t->nPatches + 1) *sizeof (char*));
t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*));
if (t->Data == NULL) {
SynError(it8, "AllocateDataSet: Unable to allocate data array");
@ -1470,7 +1559,7 @@ void WriteStr(SAVESTREAM* f, const char *str)
if (str == NULL)
str = " ";
// Lenghth to write
// Length to write
len = (cmsUInt32Number) strlen(str);
f ->Used += len;
@ -1524,6 +1613,9 @@ void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
KEYVALUE* p;
TABLE* t = GetTable(it8);
// Writes the type
WriteStr(fp, t->SheetType);
WriteStr(fp, "\n");
for (p = t->HeaderList; (p != NULL); p = p->Next)
{
@ -1672,8 +1764,6 @@ cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
sd.stream = fopen(cFileName, "wt");
if (!sd.stream) return FALSE;
WriteStr(&sd, it8->SheetType);
WriteStr(&sd, "\n");
for (i=0; i < it8 ->TablesCount; i++) {
cmsIT8SetTable(hIT8, i);
@ -1708,20 +1798,18 @@ cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number*
else
sd.Max = 0; // Just counting the needed bytes
WriteStr(&sd, it8->SheetType);
WriteStr(&sd, "\n");
for (i=0; i < it8 ->TablesCount; i++) {
cmsIT8SetTable(hIT8, i);
WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8);
WriteData(&sd, it8);
cmsIT8SetTable(hIT8, i);
WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8);
WriteData(&sd, it8);
}
sd.Used++; // The \0 at the very end
if (sd.Base)
sd.Ptr = 0;
*sd.Ptr = 0;
*BytesNeeded = sd.Used;
@ -1934,12 +2022,8 @@ cmsBool HeaderSection(cmsIT8* it8)
static
cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
void ReadType(cmsIT8* it8, char* SheetTypePtr)
{
char* SheetTypePtr = it8 ->SheetType;
if (nosheet == 0) {
// First line is a very special case.
while (isseparator(it8->ch))
@ -1950,9 +2034,20 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
*SheetTypePtr++= (char) it8 ->ch;
NextCh(it8);
}
}
*SheetTypePtr = 0;
}
static
cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
{
char* SheetTypePtr = it8 ->Tab[0].SheetType;
if (nosheet == 0) {
ReadType(it8, SheetTypePtr);
}
InSymbol(it8);
SkipEOLN(it8);
@ -1974,6 +2069,39 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
AllocTable(it8);
it8 ->nTable = it8 ->TablesCount - 1;
// Read sheet type if present. We only support identifier and string.
// <ident> <eoln> is a type string
// anything else, is not a type string
if (nosheet == 0) {
if (it8 ->sy == SIDENT) {
// May be a type sheet or may be a prop value statement. We cannot use insymbol in
// this special case...
while (isseparator(it8->ch))
NextCh(it8);
// If a newline is found, then this is a type string
if (it8 ->ch == '\n' || it8->ch == '\r') {
cmsIT8SetSheetType(it8, it8 ->id);
InSymbol(it8);
}
else
{
// It is not. Just continue
cmsIT8SetSheetType(it8, "");
}
}
else
// Validate quoted strings
if (it8 ->sy == SSTRING) {
cmsIT8SetSheetType(it8, it8 ->str);
InSymbol(it8);
}
}
}
break;
@ -2022,9 +2150,9 @@ void CookPointers(cmsIT8* it8)
if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
t -> SampleID = idField;
t -> SampleID = idField;
for (i=0; i < t -> nPatches; i++) {
for (i=0; i < t -> nPatches; i++) {
char *Data = GetData(it8, i, idField);
if (Data) {
@ -2039,7 +2167,7 @@ void CookPointers(cmsIT8* it8)
SetData(it8, i, idField, Buffer);
}
}
}
}
@ -2070,7 +2198,7 @@ void CookPointers(cmsIT8* it8)
char Buffer[256];
char *Type = p ->Value;
int nTable = k;
int nTable = (int) k;
snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type );
@ -2094,14 +2222,14 @@ void CookPointers(cmsIT8* it8)
// Try to infere if the file is a CGATS/IT8 file at all. Read first line
// that should be something like some printable characters plus a \n
// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
static
int IsMyBlock(cmsUInt8Number* Buffer, int n)
{
int cols = 1, space = 0, quot = 0;
int words = 1, space = 0, quot = 0;
int i;
if (n < 10) return FALSE; // Too small
if (n < 10) return 0; // Too small
if (n > 132)
n = 132;
@ -2112,7 +2240,7 @@ int IsMyBlock(cmsUInt8Number* Buffer, int n)
{
case '\n':
case '\r':
return quot == 1 || cols > 2 ? 0 : cols;
return ((quot == 1) || (words > 2)) ? 0 : words;
case '\t':
case ' ':
if(!quot && !space)
@ -2124,14 +2252,13 @@ int IsMyBlock(cmsUInt8Number* Buffer, int n)
default:
if (Buffer[i] < 32) return 0;
if (Buffer[i] > 127) return 0;
cols += space;
words += space;
space = 0;
break;
}
}
return FALSE;
return 0;
}
@ -2151,7 +2278,7 @@ cmsBool IsMyFile(const char* FileName)
Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
if (fclose(fp) != 0)
return FALSE;
return FALSE;
Ptr[Size] = '\0';
@ -2165,10 +2292,10 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt3
{
cmsHANDLE hIT8;
cmsIT8* it8;
int type;
int type;
_cmsAssert(Ptr != NULL);
_cmsAssert(len != 0);
_cmsAssert(Ptr != NULL);
_cmsAssert(len != 0);
type = IsMyBlock((cmsUInt8Number*)Ptr, len);
if (type == 0) return NULL;
@ -2208,11 +2335,11 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileN
cmsHANDLE hIT8;
cmsIT8* it8;
int type;
int type;
_cmsAssert(cFileName != NULL);
_cmsAssert(cFileName != NULL);
type = IsMyFile(cFileName);
type = IsMyFile(cFileName);
if (type == 0) return NULL;
hIT8 = cmsIT8Alloc(ContextID);
@ -2241,10 +2368,10 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileN
CookPointers(it8);
it8 ->nTable = 0;
if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
cmsIT8Free(hIT8);
if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
cmsIT8Free(hIT8);
return NULL;
}
}
return hIT8;
@ -2252,16 +2379,16 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileN
int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t;
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
t = GetTable(it8);
if (SampleNames)
*SampleNames = t -> DataFormat;
return t -> nSamples;
if (SampleNames)
*SampleNames = t -> DataFormat;
return t -> nSamples;
}
@ -2273,9 +2400,9 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyN
char **Props;
TABLE* t;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
t = GetTable(it8);
// Pass#1 - count properties
@ -2305,10 +2432,10 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cP
const char **Props;
TABLE* t;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
t = GetTable(it8);
if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
*SubpropertyNames = 0;
@ -2402,7 +2529,7 @@ int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
return LocateSample(it8, cSample);
}
@ -2413,7 +2540,7 @@ const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
return GetData(it8, row, col);
}
@ -2425,13 +2552,9 @@ cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int c
Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
if (Buffer) {
return atof(Buffer);
} else
return 0;
if (Buffer == NULL) return 0.0;
return ParseFloatNumber(Buffer);
}
@ -2439,7 +2562,7 @@ cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const ch
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
return SetData(it8, row, col, Val);
}
@ -2450,7 +2573,7 @@ cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFl
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256];
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
sprintf(Buff, it8->DoubleFormatter, Val);
@ -2464,7 +2587,7 @@ const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const ch
cmsIT8* it8 = (cmsIT8*) hIT8;
int iField, iSet;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
iField = LocateSample(it8, cSample);
if (iField < 0) {
@ -2486,14 +2609,7 @@ cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch,
Buffer = cmsIT8GetData(it8, cPatch, cSample);
if (Buffer) {
return atof(Buffer);
} else {
return 0;
}
return ParseFloatNumber(Buffer);
}
@ -2504,9 +2620,9 @@ cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char*
int iField, iSet;
TABLE* t;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
t = GetTable(it8);
iField = LocateSample(it8, cSample);
@ -2541,53 +2657,53 @@ cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char*
cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
const char* cSample,
cmsFloat64Number Val)
const char* cSample,
cmsFloat64Number Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256];
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256];
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
snprintf(Buff, 255, it8->DoubleFormatter, Val);
return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
snprintf(Buff, 255, it8->DoubleFormatter, Val);
return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
}
// Buffer should get MAXSTR at least
const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t;
char* Data;
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t;
char* Data;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
Data = GetData(it8, nPatch, t->SampleID);
t = GetTable(it8);
Data = GetData(it8, nPatch, t->SampleID);
if (!Data) return NULL;
if (!buffer) return Data;
if (!Data) return NULL;
if (!buffer) return Data;
strncpy(buffer, Data, MAXSTR-1);
buffer[MAXSTR-1] = 0;
return buffer;
strncpy(buffer, Data, MAXSTR-1);
buffer[MAXSTR-1] = 0;
return buffer;
}
int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
{
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
return LocatePatch((cmsIT8*)hIT8, cPatch);
}
cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
return it8 ->TablesCount;
return it8 ->TablesCount;
}
// This handles the "LABEL" extension.
@ -2627,17 +2743,17 @@ int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char
cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
int pos;
cmsIT8* it8 = (cmsIT8*) hIT8;
int pos;
_cmsAssert(hIT8 != NULL);
_cmsAssert(hIT8 != NULL);
pos = LocateSample(it8, cSample);
if(pos == -1)
return FALSE;
pos = LocateSample(it8, cSample);
if(pos == -1)
return FALSE;
it8->Tab[it8->nTable].SampleID = pos;
return TRUE;
it8->Tab[it8->nTable].SampleID = pos;
return TRUE;
}
@ -2650,6 +2766,8 @@ void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
if (Formatter == NULL)
strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
else
strcpy(it8->DoubleFormatter, Formatter);
strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));
it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -108,15 +108,68 @@ static cmsIntentsList DefaultIntents[] = {
// A pointer to the begining of the list
static cmsIntentsList *Intents = DefaultIntents;
_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL };
// Duplicates the zone of memory used by the plug-in in the new context
static
void DupPluginIntentsList(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
_cmsIntentsPluginChunkType newHead = { NULL };
cmsIntentsList* entry;
cmsIntentsList* Anterior = NULL;
_cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin];
// Walk the list copying all nodes
for (entry = head->Intents;
entry != NULL;
entry = entry ->Next) {
cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList));
if (newEntry == NULL)
return;
// We want to keep the linked list order, so this is a little bit tricky
newEntry -> Next = NULL;
if (Anterior)
Anterior -> Next = newEntry;
Anterior = newEntry;
if (newHead.Intents == NULL)
newHead.Intents = newEntry;
}
ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType));
}
void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
if (src != NULL) {
// Copy all linked list
DupPluginIntentsList(ctx, src);
}
else {
static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL };
ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType));
}
}
// Search the list for a suitable intent. Returns NULL if not found
static
cmsIntentsList* SearchIntent(cmsUInt32Number Intent)
cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent)
{
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin);
cmsIntentsList* pt;
for (pt = Intents; pt != NULL; pt = pt -> Next)
for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next)
if (pt ->Intent == Intent) return pt;
for (pt = DefaultIntents; pt != NULL; pt = pt -> Next)
if (pt ->Intent == Intent) return pt;
return NULL;
@ -164,17 +217,21 @@ void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn,
static
cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
{
// Convert D50 across CHAD to get the absolute white point
cmsVEC3 d, s;
cmsCIEXYZ Dest;
cmsCIExyY DestChromaticity;
cmsFloat64Number TempK;
// Convert D50 across inverse CHAD to get the absolute white point
cmsVEC3 d, s;
cmsCIEXYZ Dest;
cmsCIExyY DestChromaticity;
cmsFloat64Number TempK;
cmsMAT3 m1, m2;
m1 = *Chad;
if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
s.n[VX] = cmsD50_XYZ() -> X;
s.n[VY] = cmsD50_XYZ() -> Y;
s.n[VZ] = cmsD50_XYZ() -> Z;
_cmsMAT3eval(&d, Chad, &s);
_cmsMAT3eval(&d, &m2, &s);
Dest.X = d.n[VX];
Dest.Y = d.n[VY];
@ -190,15 +247,14 @@ cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
// Compute a CHAD based on a given temperature
static
void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
{
cmsCIEXYZ White;
cmsCIExyY ChromaticityOfWhite;
cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp);
cmsxyY2XYZ(&White, &ChromaticityOfWhite);
_cmsAdaptationMatrix(Chad, NULL, cmsD50_XYZ(), &White);
_cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ());
}
// Join scalings to obtain relative input to absolute and then to relative output.
@ -211,7 +267,7 @@ cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
const cmsMAT3* ChromaticAdaptationMatrixOut,
cmsMAT3* m)
{
cmsMAT3 Scale, m1, m2, m3;
cmsMAT3 Scale, m1, m2, m3, m4;
// Adaptation state
if (AdaptationState == 1.0) {
@ -230,23 +286,32 @@ cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
_cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0);
_cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z);
m1 = *ChromaticAdaptationMatrixIn;
if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
_cmsMAT3per(&m3, &m2, &Scale);
// m3 holds CHAD from input white to D50 times abs. col. scaling
if (AdaptationState == 0.0) {
m1 = *ChromaticAdaptationMatrixOut;
_cmsMAT3per(&m2, &m1, &Scale);
// m2 holds CHAD from output white to D50 times abs. col. scaling
// Observer is not adapted, undo the chromatic adaptation
_cmsMAT3per(m, &m3, ChromaticAdaptationMatrixOut);
_cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut);
m3 = *ChromaticAdaptationMatrixIn;
if (!_cmsMAT3inverse(&m3, &m4)) return FALSE;
_cmsMAT3per(m, &m2, &m4);
} else {
cmsMAT3 MixedCHAD;
cmsFloat64Number TempSrc, TempDest, Temp;
TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); // K for source white
TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); // K for dest white
m1 = *ChromaticAdaptationMatrixIn;
if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
_cmsMAT3per(&m3, &m2, &Scale);
// m3 holds CHAD from input white to D50 times abs. col. scaling
TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn);
TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut);
if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong
@ -256,9 +321,9 @@ cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
return TRUE;
}
Temp = AdaptationState * TempSrc + (1.0 - AdaptationState) * TempDest;
Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc;
// Get a CHAD from D50 to whatever output temperature. This replaces output CHAD
// Get a CHAD from whatever output temperature to D50. This replaces output CHAD
Temp2CHAD(&MixedCHAD, Temp);
_cmsMAT3per(m, &m3, &MixedCHAD);
@ -333,7 +398,7 @@ cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[],
cmsCIEXYZ BlackPointIn, BlackPointOut;
cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0);
cmsDetectBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0);
cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0);
// If black points are equal, then do nothing
if (BlackPointIn.X != BlackPointOut.X ||
@ -370,57 +435,61 @@ cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColo
// Handle PCS mismatches. A specialized stage is added to the LUT in such case
switch (InPCS) {
case cmsSigXYZData: // Input profile operates in XYZ
case cmsSigXYZData: // Input profile operates in XYZ
switch (OutPCS) {
switch (OutPCS) {
case cmsSigXYZData: // XYZ -> XYZ
if (!IsEmptyLayer(m, off))
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
break;
case cmsSigXYZData: // XYZ -> XYZ
if (!IsEmptyLayer(m, off) &&
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
return FALSE;
break;
case cmsSigLabData: // XYZ -> Lab
if (!IsEmptyLayer(m, off))
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID));
break;
case cmsSigLabData: // XYZ -> Lab
if (!IsEmptyLayer(m, off) &&
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
return FALSE;
if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID)))
return FALSE;
break;
default:
return FALSE; // Colorspace mismatch
}
break;
default:
return FALSE; // Colorspace mismatch
}
break;
case cmsSigLabData: // Input profile operates in Lab
case cmsSigLabData: // Input profile operates in Lab
switch (OutPCS) {
switch (OutPCS) {
case cmsSigXYZData: // Lab -> XYZ
case cmsSigXYZData: // Lab -> XYZ
if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)))
return FALSE;
if (!IsEmptyLayer(m, off) &&
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
return FALSE;
break;
cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID));
if (!IsEmptyLayer(m, off))
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
break;
case cmsSigLabData: // Lab -> Lab
case cmsSigLabData: // Lab -> Lab
if (!IsEmptyLayer(m, off)) {
cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID));
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID));
}
break;
default:
return FALSE; // Mismatch
if (!IsEmptyLayer(m, off)) {
if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) ||
!cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) ||
!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID)))
return FALSE;
}
break;
// On colorspaces other than PCS, check for same space
default:
if (InPCS != OutPCS) return FALSE;
break;
return FALSE; // Mismatch
}
break;
// On colorspaces other than PCS, check for same space
default:
if (InPCS != OutPCS) return FALSE;
break;
}
return TRUE;
@ -434,6 +503,10 @@ cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature
// If they are same, they are compatible.
if (a == b) return TRUE;
// Check for MCH4 substitution of CMYK
if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE;
if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE;
// Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other.
if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE;
if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE;
@ -452,7 +525,8 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags)
{
cmsPipeline* Lut, *Result;
cmsPipeline* Lut = NULL;
cmsPipeline* Result;
cmsHPROFILE hProfile;
cmsMAT3 m;
cmsVEC3 off;
@ -460,8 +534,8 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
cmsProfileClassSignature ClassSig;
cmsUInt32Number i, Intent;
// For safety
if (nProfiles == 0) return NULL;
// For safety
if (nProfiles == 0) return NULL;
// Allocate an empty LUT for holding the result. 0 as channel count means 'undefined'
Result = cmsPipelineAlloc(ContextID, 0, 0);
@ -478,14 +552,14 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass );
// First profile is used as input unless devicelink or abstract
if ((i == 0) && !lIsDeviceLink) {
lIsInput = TRUE;
}
else {
// Else use profile in the input direction if current space is not PCS
if ((i == 0) && !lIsDeviceLink) {
lIsInput = TRUE;
}
else {
// Else use profile in the input direction if current space is not PCS
lIsInput = (CurrentColorSpace != cmsSigXYZData) &&
(CurrentColorSpace != cmsSigLabData);
}
}
Intent = TheIntents[i];
@ -508,7 +582,7 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
// If devicelink is found, then no custom intent is allowed and we can
// read the LUT to be applied. Settings don't apply here.
if (lIsDeviceLink) {
if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) {
// Get the involved LUT from the profile
Lut = _cmsReadDevicelinkLUT(hProfile, Intent);
@ -548,8 +622,11 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
}
// Concatenate to the output LUT
cmsPipelineCat(Result, Lut);
if (!cmsPipelineCat(Result, Lut))
goto Error;
cmsPipelineFree(Lut);
Lut = NULL;
// Update current space
CurrentColorSpace = ColorSpaceOut;
@ -559,6 +636,7 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
Error:
if (Lut != NULL) cmsPipelineFree(Lut);
if (Result != NULL) cmsPipelineFree(Result);
return NULL;
@ -697,7 +775,8 @@ cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
if (CLUT == NULL) goto Error;
// This is the one and only MPE in this LUT
cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT);
if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT))
goto Error;
// Sample it. We cannot afford pre/post linearization this time.
if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0))
@ -847,7 +926,8 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
// Check for non-cmyk profiles
if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData ||
cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData)
!(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData ||
cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass))
return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
// Allocate an empty LUT for holding the result
@ -864,6 +944,8 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
// Get total area coverage (in 0..1 domain)
bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0;
if (bp.MaxTAC <= 0) goto Cleanup;
// Create a LUT holding normal ICC transform
bp.cmyk2cmyk = DefaultICCintents(ContextID,
@ -873,6 +955,7 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
BPC,
AdaptationStates,
dwFlags);
if (bp.cmyk2cmyk == NULL) goto Cleanup;
// Now the tone curve
bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles,
@ -881,7 +964,7 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
BPC,
AdaptationStates,
dwFlags);
if (bp.KTone == NULL) goto Cleanup;
// To measure the output, Last profile to Lab
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
@ -889,13 +972,15 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
if ( bp.hProofOutput == NULL) goto Cleanup;
// Same as anterior, but lab in the 0..1 range
bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1],
FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab,
FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4),
FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4),
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
if (bp.cmyk2Lab == NULL) goto Cleanup;
cmsCloseProfile(hLab);
// Error estimation (for debug only)
@ -908,7 +993,8 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL);
if (CLUT == NULL) goto Cleanup;
cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT);
if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT))
goto Cleanup;
cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0);
@ -969,11 +1055,11 @@ cmsPipeline* _cmsLinkProfiles(cmsContext ContextID,
// this case would present some issues if the custom intent tries to do things like
// preserve primaries. This solution is not perfect, but works well on most cases.
Intent = SearchIntent(TheIntents[0]);
if (Intent == NULL) {
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]);
return NULL;
}
Intent = SearchIntent(ContextID, TheIntents[0]);
if (Intent == NULL) {
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]);
return NULL;
}
// Call the handler
return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags);
@ -984,12 +1070,14 @@ cmsPipeline* _cmsLinkProfiles(cmsContext ContextID,
// Get information about available intents. nMax is the maximum space for the supplied "Codes"
// and "Descriptions" the function returns the total number of intents, which may be greater
// than nMax, although the matrices are not populated beyond this level.
cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
{
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin);
cmsIntentsList* pt;
cmsUInt32Number nIntents;
for (nIntents=0, pt = Intents; pt != NULL; pt = pt -> Next)
for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
if (Codes != NULL)
@ -1002,37 +1090,52 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32
nIntents++;
}
for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
if (Codes != NULL)
Codes[nIntents] = pt ->Intent;
if (Descriptions != NULL)
Descriptions[nIntents] = pt ->Description;
}
nIntents++;
}
return nIntents;
}
// The plug-in registration. User can add new intents or override default routines
cmsBool _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Data)
cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
{
return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions);
}
// The plug-in registration. User can add new intents or override default routines
cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data)
{
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin);
cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data;
cmsIntentsList* fl;
// Do we have to reset the intents?
// Do we have to reset the custom intents?
if (Data == NULL) {
Intents = DefaultIntents;
return TRUE;
ctx->Intents = NULL;
return TRUE;
}
fl = SearchIntent(Plugin ->Intent);
fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList));
if (fl == NULL) return FALSE;
if (fl == NULL) {
fl = (cmsIntentsList*) _cmsPluginMalloc(sizeof(cmsIntentsList));
if (fl == NULL) return FALSE;
}
fl ->Intent = Plugin ->Intent;
strncpy(fl ->Description, Plugin ->Description, 255);
fl ->Description[255] = 0;
strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1);
fl ->Description[sizeof(fl ->Description)-1] = 0;
fl ->Link = Plugin ->Link;
fl ->Next = Intents;
Intents = fl;
fl ->Next = ctx ->Intents;
ctx ->Intents = fl;
return TRUE;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -31,13 +31,14 @@
// compare two strings ignoring case
int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
{
register const unsigned char *us1 = (const unsigned char *)s1,
*us2 = (const unsigned char *)s2;
register const unsigned char *us1 = (const unsigned char *)s1,
*us2 = (const unsigned char *)s2;
while (toupper(*us1) == toupper(*us2++))
if (*us1++ == '\0')
return (0);
return (toupper(*us1) - toupper(*--us2));
while (toupper(*us1) == toupper(*us2++))
if (*us1++ == '\0')
return 0;
return (toupper(*us1) - toupper(*--us2));
}
// long int because C99 specifies ftell in such way (7.19.9.2)
@ -47,9 +48,9 @@ long int CMSEXPORT cmsfilelength(FILE* f)
p = ftell(f); // register current file position
if (fseek(f, 0, SEEK_END) != 0) {
return -1;
}
if (fseek(f, 0, SEEK_END) != 0) {
return -1;
}
n = ftell(f);
fseek(f, p, SEEK_SET); // file position restored
@ -62,9 +63,8 @@ long int CMSEXPORT cmsfilelength(FILE* f)
//
// 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.
// amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent
// bogus or evil code to allocate huge blocks that otherwise lcms would never need.
#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U))
@ -74,7 +74,7 @@ long int CMSEXPORT cmsfilelength(FILE* f)
// 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);
cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// *********************************************************************************
@ -114,7 +114,7 @@ void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr)
cmsUNUSED_PARAMETER(ContextID);
}
// The default realloc function. Again it check for exploits. If Ptr is NULL,
// The default realloc function. Again it checks 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)
@ -167,82 +167,132 @@ void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number si
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;
// Pointers to memory manager functions in Context0
_cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn,
_cmsReallocDefaultFn, _cmsCallocDefaultFn, _cmsDupDefaultFn
};
// Reset and duplicate memory manager
void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src)
{
_cmsAssert(ctx != NULL);
if (src != NULL) {
// Duplicate
ctx ->chunks[MemPlugin] = _cmsSubAllocDup(ctx ->MemPool, src ->chunks[MemPlugin], sizeof(_cmsMemPluginChunkType));
}
else {
// To reset it, we use the default allocators, which cannot be overriden
ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager;
}
}
// Auxiliar to fill memory management functions from plugin (or context 0 defaults)
void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr)
{
if (Plugin == NULL) {
memcpy(ptr, &_cmsMemPluginChunk, sizeof(_cmsMemPluginChunk));
}
else {
ptr ->MallocPtr = Plugin -> MallocPtr;
ptr ->FreePtr = Plugin -> FreePtr;
ptr ->ReallocPtr = Plugin -> ReallocPtr;
// Make sure we revert to defaults
ptr ->MallocZeroPtr= _cmsMallocZeroDefaultFn;
ptr ->CallocPtr = _cmsCallocDefaultFn;
ptr ->DupPtr = _cmsDupDefaultFn;
if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr;
if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr;
if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr;
}
}
// Plug-in replacement entry
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase *Data)
cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data)
{
cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data;
_cmsMemPluginChunkType* ptr;
// NULL forces to reset to defaults
// NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure.
// Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the
// context internal data should be malloce'd by using those functions.
if (Data == NULL) {
MallocPtr = _cmsMallocDefaultFn;
MallocZeroPtr= _cmsMallocZeroDefaultFn;
FreePtr = _cmsFreeDefaultFn;
ReallocPtr = _cmsReallocDefaultFn;
CallocPtr = _cmsCallocDefaultFn;
DupPtr = _cmsDupDefaultFn;
struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID;
// Return to the default allocators
if (ContextID != NULL) {
ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager;
}
return TRUE;
}
// Check for required callbacks
if (Plugin -> MallocPtr == NULL ||
// Check for required callbacks
if (Plugin -> MallocPtr == NULL ||
Plugin -> FreePtr == NULL ||
Plugin -> ReallocPtr == NULL) return FALSE;
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;
ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
if (ptr == NULL)
return FALSE;
_cmsInstallAllocFunctions(Plugin, ptr);
return TRUE;
}
// Generic allocate
void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size)
{
return MallocPtr(ContextID, size);
_cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
return ptr ->MallocPtr(ContextID, size);
}
// Generic allocate & zero
void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size)
{
return MallocZeroPtr(ContextID, size);
_cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
return ptr->MallocZeroPtr(ContextID, size);
}
// Generic calloc
void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)
{
return CallocPtr(ContextID, num, size);
_cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
return ptr->CallocPtr(ContextID, num, size);
}
// Generic reallocate
void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
{
return ReallocPtr(ContextID, Ptr, size);
_cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
return ptr->ReallocPtr(ContextID, Ptr, size);
}
// Generic free memory
void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr)
{
if (Ptr != NULL) FreePtr(ContextID, Ptr);
if (Ptr != NULL) {
_cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
ptr ->FreePtr(ContextID, Ptr);
}
}
// Generic block duplication
void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size)
{
return DupPtr(ContextID, Org, size);
_cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
return ptr ->DupPtr(ContextID, Org, size);
}
// ********************************************************************************************
@ -250,12 +300,16 @@ void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Numbe
// 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)
// memory is being swapped to disk. This approach is safer (although that may not be true on all platforms)
static
_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
{
_cmsSubAllocator_chunk* chunk;
// 20K by default
if (Initial == 0)
Initial = 20*1024;
// Create the container
chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk));
if (chunk == NULL) return NULL;
@ -269,10 +323,6 @@ _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32N
return NULL;
}
// 20K by default
if (Initial == 0)
Initial = 20*1024;
chunk ->BlockSize = Initial;
chunk ->Used = 0;
chunk ->next = NULL;
@ -325,7 +375,7 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used;
cmsUInt8Number* ptr;
size = _cmsALIGNLONG(size);
size = _cmsALIGNMEM(size);
// Check for memory. If there is no room, allocate a new chunk of double memory size.
if (size > Free) {
@ -351,6 +401,26 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
return (void*) ptr;
}
// Duplicate in pool
void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size)
{
void *NewPtr;
// Dup of null pointer is also NULL
if (ptr == NULL)
return NULL;
NewPtr = _cmsSubAlloc(s, size);
if (ptr != NULL && NewPtr != NULL) {
memcpy(NewPtr, ptr, size);
}
return NewPtr;
}
// Error logging ******************************************************************
// There is no error handling at all. When a funtion fails, it returns proper value.
@ -372,8 +442,26 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
// 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;
// Context0 storage, which is global
_cmsLogErrorChunkType _cmsLogErrorChunk = { DefaultLogErrorHandlerFunction };
// Allocates and inits error logger container for a given context. If src is NULL, only initializes the value
// to the default. Otherwise, it duplicates the value. The interface is standard across all context clients
void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
static _cmsLogErrorChunkType LogErrorChunk = { DefaultLogErrorHandlerFunction };
void* from;
if (src != NULL) {
from = src ->chunks[Logger];
}
else {
from = &LogErrorChunk;
}
ctx ->chunks[Logger] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsLogErrorChunkType));
}
// The default error logger does nothing.
static
@ -384,16 +472,27 @@ void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorC
cmsUNUSED_PARAMETER(ContextID);
cmsUNUSED_PARAMETER(ErrorCode);
cmsUNUSED_PARAMETER(Text);
cmsUNUSED_PARAMETER(Text);
}
// Change log error
// Change log error, context based
void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn)
{
_cmsLogErrorChunkType* lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger);
if (lhg != NULL) {
if (Fn == NULL)
lhg -> LogErrorHandler = DefaultLogErrorHandlerFunction;
else
lhg -> LogErrorHandler = Fn;
}
}
// Change log error, legacy
void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn)
{
if (Fn == NULL)
LogErrorHandler = DefaultLogErrorHandlerFunction;
else
LogErrorHandler = Fn;
cmsSetLogErrorHandlerTHR(NULL, Fn);
}
// Log an error
@ -402,13 +501,18 @@ void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, c
{
va_list args;
char Buffer[MAX_ERROR_MESSAGE_LEN];
_cmsLogErrorChunkType* lhg;
va_start(args, ErrorText);
vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args);
va_end(args);
// Call handler
LogErrorHandler(ContextID, ErrorCode, Buffer);
// Check for the context, if specified go there. If not, go for the global
lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger);
if (lhg ->LogErrorHandler) {
lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer);
}
}
// Utility function to print signatures
@ -426,3 +530,125 @@ void _cmsTagSignature2String(char String[5], cmsTagSignature sig)
String[4] = 0;
}
//--------------------------------------------------------------------------------------------------
static
void* defMtxCreate(cmsContext id)
{
_cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex));
_cmsInitMutexPrimitive(ptr_mutex);
return (void*) ptr_mutex;
}
static
void defMtxDestroy(cmsContext id, void* mtx)
{
_cmsDestroyMutexPrimitive((_cmsMutex *) mtx);
_cmsFree(id, mtx);
}
static
cmsBool defMtxLock(cmsContext id, void* mtx)
{
cmsUNUSED_PARAMETER(id);
return _cmsLockPrimitive((_cmsMutex *) mtx) == 0;
}
static
void defMtxUnlock(cmsContext id, void* mtx)
{
cmsUNUSED_PARAMETER(id);
_cmsUnlockPrimitive((_cmsMutex *) mtx);
}
// Pointers to memory manager functions in Context0
_cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock };
// Allocate and init mutex container.
void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock };
void* from;
if (src != NULL) {
from = src ->chunks[MutexPlugin];
}
else {
from = &MutexChunk;
}
ctx ->chunks[MutexPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsMutexPluginChunkType));
}
// Register new ways to transform
cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data)
{
cmsPluginMutex* Plugin = (cmsPluginMutex*) Data;
_cmsMutexPluginChunkType* ctx = ( _cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
if (Data == NULL) {
// No lock routines
ctx->CreateMutexPtr = NULL;
ctx->DestroyMutexPtr = NULL;
ctx->LockMutexPtr = NULL;
ctx ->UnlockMutexPtr = NULL;
return TRUE;
}
// Factory callback is required
if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL ||
Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE;
ctx->CreateMutexPtr = Plugin->CreateMutexPtr;
ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr;
ctx ->LockMutexPtr = Plugin ->LockMutexPtr;
ctx ->UnlockMutexPtr = Plugin ->UnlockMutexPtr;
// All is ok
return TRUE;
}
// Generic Mutex fns
void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID)
{
_cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
if (ptr ->CreateMutexPtr == NULL) return NULL;
return ptr ->CreateMutexPtr(ContextID);
}
void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx)
{
_cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
if (ptr ->DestroyMutexPtr != NULL) {
ptr ->DestroyMutexPtr(ContextID, mtx);
}
}
cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx)
{
_cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
if (ptr ->LockMutexPtr == NULL) return TRUE;
return ptr ->LockMutexPtr(ContextID, mtx);
}
void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx)
{
_cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin);
if (ptr ->UnlockMutexPtr != NULL) {
ptr ->UnlockMutexPtr(ContextID, mtx);
}
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2013 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"),
@ -53,7 +53,6 @@ typedef struct _cmsParametricCurvesCollection_st {
} _cmsParametricCurvesCollection;
// This is the default (built-in) evaluator
static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R);
@ -66,22 +65,77 @@ static _cmsParametricCurvesCollection DefaultCurves = {
NULL // Next in chain
};
// Duplicates the zone of memory used by the plug-in in the new context
static
void DupPluginCurvesList(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
_cmsCurvesPluginChunkType newHead = { NULL };
_cmsParametricCurvesCollection* entry;
_cmsParametricCurvesCollection* Anterior = NULL;
_cmsCurvesPluginChunkType* head = (_cmsCurvesPluginChunkType*) src->chunks[CurvesPlugin];
_cmsAssert(head != NULL);
// Walk the list copying all nodes
for (entry = head->ParametricCurves;
entry != NULL;
entry = entry ->Next) {
_cmsParametricCurvesCollection *newEntry = ( _cmsParametricCurvesCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsParametricCurvesCollection));
if (newEntry == NULL)
return;
// We want to keep the linked list order, so this is a little bit tricky
newEntry -> Next = NULL;
if (Anterior)
Anterior -> Next = newEntry;
Anterior = newEntry;
if (newHead.ParametricCurves == NULL)
newHead.ParametricCurves = newEntry;
}
ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsCurvesPluginChunkType));
}
// The allocator have to follow the chain
void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
_cmsAssert(ctx != NULL);
if (src != NULL) {
// Copy all linked list
DupPluginCurvesList(ctx, src);
}
else {
static _cmsCurvesPluginChunkType CurvesPluginChunk = { NULL };
ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx ->MemPool, &CurvesPluginChunk, sizeof(_cmsCurvesPluginChunkType));
}
}
// The linked list head
static _cmsParametricCurvesCollection* ParametricCurves = &DefaultCurves;
_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL };
// As a way to install new parametric curves
cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Data)
cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data)
{
_cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);
cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data;
_cmsParametricCurvesCollection* fl;
if (Data == NULL) {
ParametricCurves = &DefaultCurves;
ctx -> ParametricCurves = NULL;
return TRUE;
}
fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(sizeof(_cmsParametricCurvesCollection));
fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection));
if (fl == NULL) return FALSE;
// Copy the parameters
@ -97,8 +151,8 @@ cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Data)
memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number));
// Keep linked list
fl ->Next = ParametricCurves;
ParametricCurves = fl;
fl ->Next = ctx->ParametricCurves;
ctx->ParametricCurves = fl;
// All is ok
return TRUE;
@ -120,12 +174,24 @@ int IsInSet(int Type, _cmsParametricCurvesCollection* c)
// Search for the collection which contains a specific type
static
_cmsParametricCurvesCollection *GetParametricCurveByType(int Type, int* index)
_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)
{
_cmsParametricCurvesCollection* c;
int Position;
_cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin);
for (c = ParametricCurves; c != NULL; c = c ->Next) {
for (c = ctx->ParametricCurves; c != NULL; c = c ->Next) {
Position = IsInSet(Type, c);
if (Position != -1) {
if (index != NULL)
*index = Position;
return c;
}
}
// If none found, revert for defaults
for (c = &DefaultCurves; c != NULL; c = c ->Next) {
Position = IsInSet(Type, c);
@ -222,14 +288,15 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntr
p ->Segments[i].SampledPoints = NULL;
c = GetParametricCurveByType(Segments[i].Type, NULL);
c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL);
if (c != NULL)
p ->Evals[i] = c ->Evaluator;
}
}
p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS);
return p;
if (p->InterpParams != NULL)
return p;
Error:
if (p -> Segments) _cmsFree(ContextID, p ->Segments);
@ -248,18 +315,28 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
switch (Type) {
// X = Y ^ Gamma
// X = Y ^ Gamma
case 1:
if (R < 0)
Val = 0;
if (R < 0) {
if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)
Val = R;
else
Val = 0;
}
else
Val = pow(R, Params[0]);
break;
// Type 1 Reversed: X = Y ^1/gamma
case -1:
if (R < 0)
Val = 0;
if (R < 0) {
if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)
Val = R;
else
Val = 0;
}
else
Val = pow(R, 1/Params[0]);
break;
@ -384,7 +461,7 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
if (e > 0)
Val = pow(e, Params[0]) + Params[5];
else
Val = 0;
Val = Params[5];
}
else
Val = R*Params[3] + Params[6];
@ -419,7 +496,7 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
e = Params[1]*R + Params[2];
if (e < 0)
Val = 0;
Val = Params[3];
else
Val = pow(e, Params[0]) + Params[3];
break;
@ -439,7 +516,7 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
e = Params[2] * pow(R, Params[0]) + Params[3];
if (e <= 0)
Val = 0;
Val = Params[4];
else
Val = Params[1]*log10(e) + Params[4];
break;
@ -505,7 +582,7 @@ cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R)
// Type == 0 means segment is sampled
if (g ->Segments[i].Type == 0) {
cmsFloat32Number R1 = (cmsFloat32Number) (R - g ->Segments[i].x0);
cmsFloat32Number R1 = (cmsFloat32Number) (R - g ->Segments[i].x0) / (g ->Segments[i].x1 - g ->Segments[i].x0);
cmsFloat32Number Out;
// Setup the table (TODO: clean that)
@ -523,6 +600,19 @@ cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R)
return MINUS_INF;
}
// Access to estimated low-res table
cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t)
{
_cmsAssert(t != NULL);
return t ->nEntries;
}
const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t)
{
_cmsAssert(t != NULL);
return t ->Table16;
}
// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the
// floating point description empty.
@ -577,20 +667,21 @@ cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID,
// Use a segmented curve to store the floating point table
cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[])
{
cmsCurveSegment Seg[2];
cmsCurveSegment Seg[3];
// Initialize segmented curve part up to 0
Seg[0].x0 = -1;
// A segmented tone curve should have function segments in the first and last positions
// Initialize segmented curve part up to 0 to constant value = samples[0]
Seg[0].x0 = MINUS_INF;
Seg[0].x1 = 0;
Seg[0].Type = 6;
Seg[0].Params[0] = 1;
Seg[0].Params[1] = 0;
Seg[0].Params[2] = 0;
Seg[0].Params[3] = 0;
Seg[0].Params[3] = values[0];
Seg[0].Params[4] = 0;
// From zero to any
// From zero to 1
Seg[1].x0 = 0;
Seg[1].x1 = 1.0;
Seg[1].Type = 0;
@ -598,7 +689,19 @@ cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cm
Seg[1].nGridPoints = nEntries;
Seg[1].SampledPoints = (cmsFloat32Number*) values;
return cmsBuildSegmentedToneCurve(ContextID, 2, Seg);
// Final segment is constant = lastsample
Seg[2].x0 = 1.0;
Seg[2].x1 = PLUS_INF;
Seg[2].Type = 6;
Seg[2].Params[0] = 1;
Seg[2].Params[1] = 0;
Seg[2].Params[2] = 0;
Seg[2].Params[3] = values[nEntries-1];
Seg[2].Params[4] = 0;
return cmsBuildSegmentedToneCurve(ContextID, 3, Seg);
}
// Parametric curves
@ -611,12 +714,12 @@ cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt
cmsCurveSegment Seg0;
int Pos = 0;
cmsUInt32Number size;
_cmsParametricCurvesCollection* c = GetParametricCurveByType(Type, &Pos);
_cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);
_cmsAssert(Params != NULL);
if (c == NULL) {
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type);
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type);
return NULL;
}
@ -806,7 +909,10 @@ cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, con
_cmsAssert(InCurve != NULL);
// Try to reverse it analytically whatever possible
if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 && InCurve -> Segments[0].Type <= 5) {
if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 &&
/* InCurve -> Segments[0].Type <= 5 */
GetParametricCurveByType(InCurve ->InterpParams->ContextID, InCurve ->Segments[0].Type, NULL) != NULL) {
return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID,
-(InCurve -> Segments[0].Type),
@ -941,7 +1047,7 @@ cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda
if (Tab == NULL) return FALSE;
if (cmsIsToneCurveLinear(Tab)) return FALSE; // Nothing to do
if (cmsIsToneCurveLinear(Tab)) return TRUE; // Nothing to do
nItems = Tab -> nEntries;
@ -968,11 +1074,20 @@ cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda
if (z[i] == 0.) Zeros++;
if (z[i] >= 65535.) Poles++;
if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
if (z[i] < z[i-1]) {
cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic.");
return FALSE;
}
}
if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros
if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles
if (Zeros > (nItems / 3)) {
cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros.");
return FALSE;
}
if (Poles > (nItems / 3)) {
cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles.");
return FALSE;
}
// Seems ok
for (i=0; i < nItems; i++) {
@ -1008,20 +1123,42 @@ cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t)
{
int n;
int i, last;
cmsBool lDescending;
_cmsAssert(t != NULL);
n = t ->nEntries;
last = t ->Table16[n-1];
// Degenerated curves are monotonic? Ok, let's pass them
n = t ->nEntries;
if (n < 2) return TRUE;
for (i = n-2; i >= 0; --i) {
// Curve direction
lDescending = cmsIsToneCurveDescending(t);
if (t ->Table16[i] > last)
if (lDescending) {
return FALSE;
else
last = t ->Table16[i];
last = t ->Table16[0];
for (i = 1; i < n; i++) {
if (t ->Table16[i] - last > 2) // We allow some ripple
return FALSE;
else
last = t ->Table16[i];
}
}
else {
last = t ->Table16[n-1];
for (i = n-2; i >= 0; --i) {
if (t ->Table16[i] - last > 2)
return FALSE;
else
last = t ->Table16[i];
}
}
return TRUE;

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -162,7 +162,7 @@ cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
out = ComputeKToLstar(ContextID, nPoints, 1,
Intents + (nProfiles - 1),
hProfiles + (nProfiles - 1),
&hProfiles [nProfiles - 1],
BPC + (nProfiles - 1),
AdaptationStates + (nProfiles - 1),
dwFlags);
@ -183,7 +183,6 @@ cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
// Make sure it is monotonic
if (!cmsIsToneCurveMonotonic(KTone)) {
cmsFreeToneCurve(KTone);
return NULL;
}
@ -221,13 +220,10 @@ int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number O
cmsFloat64Number dE1, dE2, ErrorRatio;
// Assume in-gamut by default.
dE1 = 0.;
dE2 = 0;
ErrorRatio = 1.0;
// Convert input to Lab
if (t -> hInput != NULL)
cmsDoTransform(t -> hInput, In, &LabIn1, 1);
cmsDoTransform(t -> hInput, In, &LabIn1, 1);
// converts from PCS to colorant. This always
// does return in-gamut values,
@ -239,7 +235,7 @@ int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number O
memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab));
// Try again, but this time taking Check as input
cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1);
cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1);
cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1);
// Take difference of direct value
@ -289,126 +285,129 @@ int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number O
// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should.
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsUInt32Number Intents[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number nGamutPCSposition,
cmsHPROFILE hGamut)
cmsHPROFILE hProfiles[],
cmsBool BPC[],
cmsUInt32Number Intents[],
cmsFloat64Number AdaptationStates[],
cmsUInt32Number nGamutPCSposition,
cmsHPROFILE hGamut)
{
cmsHPROFILE hLab;
cmsPipeline* Gamut;
cmsStage* CLUT;
cmsUInt32Number dwFormat;
GAMUTCHAIN Chain;
int nChannels, nGridpoints;
cmsColorSpaceSignature ColorSpace;
cmsUInt32Number i;
cmsHPROFILE ProfileList[256];
cmsBool BPCList[256];
cmsFloat64Number AdaptationList[256];
cmsUInt32Number IntentList[256];
cmsHPROFILE hLab;
cmsPipeline* Gamut;
cmsStage* CLUT;
cmsUInt32Number dwFormat;
GAMUTCHAIN Chain;
int nChannels, nGridpoints;
cmsColorSpaceSignature ColorSpace;
cmsUInt32Number i;
cmsHPROFILE ProfileList[256];
cmsBool BPCList[256];
cmsFloat64Number AdaptationList[256];
cmsUInt32Number IntentList[256];
memset(&Chain, 0, sizeof(GAMUTCHAIN));
memset(&Chain, 0, sizeof(GAMUTCHAIN));
if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition);
return NULL;
}
if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition);
return NULL;
}
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
if (hLab == NULL) return NULL;
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
if (hLab == NULL) return NULL;
// The figure of merit. On matrix-shaper profiles, should be almost zero as
// the conversion is pretty exact. On LUT based profiles, different resolutions
// of input and output CLUT may result in differences.
// The figure of merit. On matrix-shaper profiles, should be almost zero as
// the conversion is pretty exact. On LUT based profiles, different resolutions
// of input and output CLUT may result in differences.
if (cmsIsMatrixShaper(hGamut)) {
if (cmsIsMatrixShaper(hGamut)) {
Chain.Thereshold = 1.0;
}
else {
Chain.Thereshold = ERR_THERESHOLD;
}
Chain.Thereshold = 1.0;
}
else {
Chain.Thereshold = ERR_THERESHOLD;
}
// Create a copy of parameters
for (i=0; i < nGamutPCSposition; i++) {
ProfileList[i] = hProfiles[i];
BPCList[i] = BPC[i];
AdaptationList[i] = AdaptationStates[i];
IntentList[i] = Intents[i];
}
// Create a copy of parameters
for (i=0; i < nGamutPCSposition; i++) {
ProfileList[i] = hProfiles[i];
BPCList[i] = BPC[i];
AdaptationList[i] = AdaptationStates[i];
IntentList[i] = Intents[i];
}
// Fill Lab identity
ProfileList[nGamutPCSposition] = hLab;
BPCList[nGamutPCSposition] = 0;
AdaptationList[nGamutPCSposition] = 1.0;
Intents[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC;
// Fill Lab identity
ProfileList[nGamutPCSposition] = hLab;
BPCList[nGamutPCSposition] = 0;
AdaptationList[nGamutPCSposition] = 1.0;
IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC;
ColorSpace = cmsGetColorSpace(hGamut);
ColorSpace = cmsGetColorSpace(hGamut);
nChannels = cmsChannelsOf(ColorSpace);
nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
nChannels = cmsChannelsOf(ColorSpace);
nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
// 16 bits to Lab double
Chain.hInput = cmsCreateExtendedTransform(ContextID,
nGamutPCSposition + 1,
ProfileList,
BPCList,
Intents,
AdaptationList,
NULL, 0,
dwFormat, TYPE_Lab_DBL,
cmsFLAGS_NOCACHE);
// 16 bits to Lab double
Chain.hInput = cmsCreateExtendedTransform(ContextID,
nGamutPCSposition + 1,
ProfileList,
BPCList,
IntentList,
AdaptationList,
NULL, 0,
dwFormat, TYPE_Lab_DBL,
cmsFLAGS_NOCACHE);
// Does create the forward step. Lab double to device
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
Chain.hForward = cmsCreateTransformTHR(ContextID,
hLab, TYPE_Lab_DBL,
hGamut, dwFormat,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE);
// Does create the forward step. Lab double to device
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
Chain.hForward = cmsCreateTransformTHR(ContextID,
hLab, TYPE_Lab_DBL,
hGamut, dwFormat,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE);
// Does create the backwards step
Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat,
hLab, TYPE_Lab_DBL,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE);
// Does create the backwards step
Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat,
hLab, TYPE_Lab_DBL,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE);
// All ok?
if (Chain.hForward && Chain.hReverse) {
// All ok?
if (Chain.hInput && Chain.hForward && Chain.hReverse) {
// Go on, try to compute gamut LUT from PCS. This consist on a single channel containing
// dE when doing a transform back and forth on the colorimetric intent.
// Go on, try to compute gamut LUT from PCS. This consist on a single channel containing
// dE when doing a transform back and forth on the colorimetric intent.
Gamut = cmsPipelineAlloc(ContextID, 3, 1);
Gamut = cmsPipelineAlloc(ContextID, 3, 1);
if (Gamut != NULL) {
if (Gamut != NULL) {
CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL);
if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) {
cmsPipelineFree(Gamut);
Gamut = NULL;
}
else {
cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0);
}
}
}
else
Gamut = NULL; // Didn't work...
CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL);
cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT);
// Free all needed stuff.
if (Chain.hInput) cmsDeleteTransform(Chain.hInput);
if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
if (hLab) cmsCloseProfile(hLab);
cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0);
}
}
else
Gamut = NULL; // Didn't work...
// Free all needed stuff.
if (Chain.hInput) cmsDeleteTransform(Chain.hInput);
if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
if (hLab) cmsCloseProfile(hLab);
// And return computed hull
return Gamut;
// And return computed hull
return Gamut;
}
// Total Area Coverage estimation ----------------------------------------------------------------
@ -494,9 +493,9 @@ cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile)
GridPoints[2] = 74;
if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {
bp.MaxTAC = 0;
}
if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {
bp.MaxTAC = 0;
}
cmsDeleteTransform(bp.hRoundTrip);

535
thirdparty/liblcms2/src/cmshalf.c vendored Normal file
View File

@ -0,0 +1,535 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2012 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"
#ifndef CMS_NO_HALF_SUPPORT
// This code is inspired in the paper "Fast Half Float Conversions"
// by Jeroen van der Zijp
static cmsUInt32Number Mantissa[2048] = {
0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000,
0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,
0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000,
0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000,
0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000,
0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000,
0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000,
0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000,
0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000,
0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000,
0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000,
0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000,
0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000,
0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000,
0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000,
0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000,
0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000,
0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000,
0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000,
0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000,
0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000,
0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000,
0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000,
0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000,
0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000,
0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000,
0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000,
0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000,
0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000,
0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000,
0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000,
0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000,
0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000,
0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000,
0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000,
0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000,
0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000,
0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000,
0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000,
0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000,
0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000,
0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000,
0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000,
0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000,
0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000,
0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000,
0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000,
0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000,
0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000,
0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000,
0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000,
0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000,
0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000,
0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000,
0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000,
0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000,
0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000,
0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000,
0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000,
0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000,
0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000,
0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000,
0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000,
0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000,
0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000,
0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000,
0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000,
0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000,
0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000,
0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000,
0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000,
0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000,
0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000,
0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000,
0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000,
0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000,
0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000,
0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000,
0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000,
0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000,
0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000,
0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000,
0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000,
0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000,
0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000,
0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000,
0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000,
0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000,
0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000,
0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000,
0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000,
0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000,
0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000,
0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000,
0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000,
0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000,
0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000,
0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000,
0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000,
0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000,
0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000,
0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000,
0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000,
0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000,
0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000,
0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000,
0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000,
0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000,
0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000,
0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000,
0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000,
0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000,
0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000,
0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000,
0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000,
0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000,
0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000,
0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000,
0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000,
0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000,
0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000,
0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000,
0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000,
0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000,
0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000,
0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000,
0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000,
0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000,
0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000,
0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000,
0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000,
0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000,
0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000,
0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000,
0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000,
0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000,
0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000,
0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000,
0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000,
0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000,
0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000,
0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000,
0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000,
0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000,
0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000,
0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000,
0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000,
0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000,
0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000,
0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000,
0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000,
0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000,
0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000,
0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000,
0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000,
0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000,
0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000,
0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000,
0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000,
0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000,
0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000,
0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000,
0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000,
0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000,
0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000,
0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000,
0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000,
0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000,
0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000,
0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000,
0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000,
0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000,
0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000,
0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000,
0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000,
0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000,
0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000,
0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000,
0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000,
0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000,
0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000,
0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000,
0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000,
0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000,
0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000,
0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000,
0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000,
0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000,
0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000,
0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000,
0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000,
0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000,
0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000,
0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000,
0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000,
0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000,
0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000,
0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000,
0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000,
0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000,
0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000,
0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000,
0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000,
0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000,
0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000,
0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000,
0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000,
0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000,
0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000,
0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000,
0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000,
0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000,
0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000,
0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000,
0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000,
0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000,
0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000,
0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000,
0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000,
0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000,
0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000,
0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000,
0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000,
0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000,
0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000,
0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000,
0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000,
0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000,
0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000,
0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000,
0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000,
0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000,
0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000,
0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000,
0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000,
0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000,
0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000,
0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000,
0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000,
0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000,
0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000,
0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000,
0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000,
0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000,
0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000,
0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000,
0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000,
0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000,
0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000,
0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000,
0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000,
0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000,
0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000,
0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000,
0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000,
0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000,
0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000,
0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000,
0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000,
0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000,
0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000,
0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000,
0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000,
0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000,
0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000,
0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000,
0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000,
0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000,
0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000,
0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000,
0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000,
0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000,
0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000,
0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000,
0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000,
0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000,
0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000,
0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000,
0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000,
0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000,
0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000,
0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000,
0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000,
0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000,
0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000,
0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000,
0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000,
0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000,
0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000,
0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000,
0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000,
0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000,
0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000,
0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000,
0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000,
0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000,
0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000,
0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000,
0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000,
0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000,
0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000,
0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000,
0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000,
0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000,
0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000,
0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000,
0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000,
0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000,
0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000,
0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000,
0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000,
0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000,
0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000,
0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000,
0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000,
0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000,
0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000,
0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000,
0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000,
0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000,
0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000,
0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000,
0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000,
0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000,
0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000,
0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000,
0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000,
0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000,
0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000,
0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000,
0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000,
0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000,
0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000,
0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000,
0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000,
0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000,
0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000,
0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000,
0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000,
0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000,
0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000,
0x387fc000, 0x387fe000
};
static cmsUInt16Number Offset[64] = {
0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
0x0400, 0x0400, 0x0400, 0x0400
};
static cmsUInt32Number Exponent[64] = {
0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000,
0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000,
0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000,
0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000,
0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000,
0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000,
0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000,
0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000,
0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000,
0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000,
0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000
};
static cmsUInt16Number Base[512] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00,
0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400,
0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00,
0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001,
0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400,
0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00,
0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400,
0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
0xfc00, 0xfc00
};
static cmsUInt8Number Shift[512] = {
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13,
0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x0d
};
cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h)
{
union {
cmsFloat32Number flt;
cmsUInt32Number num;
} out;
int n = h >> 10;
out.num = Mantissa[ (h & 0x3ff) + Offset[ n ] ] + Exponent[ n ];
return out.flt;
}
cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt)
{
union {
cmsFloat32Number flt;
cmsUInt32Number num;
} in;
cmsUInt32Number n, j;
in.flt = flt;
n = in.num;
j = (n >> 23) & 0x1ff;
return (cmsUInt16Number) ((cmsUInt32Number) Base[ j ] + (( n & 0x007fffff) >> Shift[ j ]));
}
#endif

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -33,32 +33,57 @@
static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags);
// This is the default factory
static cmsInterpFnFactory Interpolators = DefaultInterpolatorsFactory;
_cmsInterpPluginChunkType _cmsInterpPluginChunk = { NULL };
// The interpolation plug-in memory chunk allocator/dup
void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src)
{
void* from;
_cmsAssert(ctx != NULL);
if (src != NULL) {
from = src ->chunks[InterpPlugin];
}
else {
static _cmsInterpPluginChunkType InterpPluginChunk = { NULL };
from = &InterpPluginChunk;
}
_cmsAssert(from != NULL);
ctx ->chunks[InterpPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsInterpPluginChunkType));
}
// Main plug-in entry
cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Data)
cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data)
{
cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data;
_cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin);
if (Data == NULL) {
Interpolators = DefaultInterpolatorsFactory;
ptr ->Interpolators = NULL;
return TRUE;
}
// Set replacement functions
Interpolators = Plugin ->InterpolatorsFactory;
ptr ->Interpolators = Plugin ->InterpolatorsFactory;
return TRUE;
}
// Set the interpolation method
cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p)
cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p)
{
// Invoke factory, possibly in the Plug-in
p ->Interpolation = Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags);
_cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin);
p ->Interpolation.Lerp16 = NULL;
// Invoke factory, possibly in the Plug-in
if (ptr ->Interpolators != NULL)
p ->Interpolation = ptr->Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags);
// If unsupported by the plug-in, go for the LittleCMS default.
// If happens only if an extern plug-in is being used
@ -69,6 +94,7 @@ cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p)
if (p ->Interpolation.Lerp16 == NULL) {
return FALSE;
}
return TRUE;
}
@ -113,7 +139,7 @@ cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID,
p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i];
if (!_cmsSetInterpolationRoutine(p)) {
if (!_cmsSetInterpolationRoutine(ContextID, p)) {
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan);
_cmsFree(ContextID, p);
return NULL;
@ -186,6 +212,11 @@ void LinLerp1D(register const cmsUInt16Number Value[],
Output[0] = LinearInterp(rest, y0, y1);
}
// To prevent out of bounds indexing
cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v)
{
return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
}
// Floating-point version of 1D interpolation
static
@ -198,13 +229,15 @@ void LinLerp1Dfloat(const cmsFloat32Number Value[],
int cell0, cell1;
const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;
val2 = fclamp(Value[0]);
// if last value...
if (Value[0] == 1.0) {
if (val2 == 1.0) {
Output[0] = LutTable[p -> Domain[0]];
return;
}
val2 = p -> Domain[0] * Value[0];
val2 *= p -> Domain[0];
cell0 = (int) floor(val2);
cell1 = (int) ceil(val2);
@ -263,13 +296,15 @@ void Eval1InputFloat(const cmsFloat32Number Value[],
cmsUInt32Number OutChan;
const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;
val2 = fclamp(Value[0]);
// if last value...
if (Value[0] == 1.0) {
if (val2 == 1.0) {
Output[0] = LutTable[p -> Domain[0]];
return;
}
val2 = p -> Domain[0] * Value[0];
val2 *= p -> Domain[0];
cell0 = (int) floor(val2);
cell1 = (int) ceil(val2);
@ -310,8 +345,8 @@ void BilinearInterpFloat(const cmsFloat32Number Input[],
dxy;
TotalOut = p -> nOutputs;
px = Input[0] * p->Domain[0];
py = Input[1] * p->Domain[1];
px = fclamp(Input[0]) * p->Domain[0];
py = fclamp(Input[1]) * p->Domain[1];
x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0;
y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;
@ -425,20 +460,9 @@ void TrilinearInterpFloat(const cmsFloat32Number Input[],
TotalOut = p -> nOutputs;
// We need some clipping here
px = Input[0];
py = Input[1];
pz = Input[2];
if (px < 0) px = 0;
if (px > 1) px = 1;
if (py < 0) py = 0;
if (py > 1) py = 1;
if (pz < 0) pz = 0;
if (pz > 1) pz = 1;
px *= p->Domain[0];
py *= p->Domain[1];
pz *= p->Domain[2];
px = fclamp(Input[0]) * p->Domain[0];
py = fclamp(Input[1]) * p->Domain[1];
pz = fclamp(Input[2]) * p->Domain[2];
x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0;
y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;
@ -580,20 +604,9 @@ void TetrahedralInterpFloat(const cmsFloat32Number Input[],
TotalOut = p -> nOutputs;
// We need some clipping here
px = Input[0];
py = Input[1];
pz = Input[2];
if (px < 0) px = 0;
if (px > 1) px = 1;
if (py < 0) py = 0;
if (py > 1) py = 1;
if (pz < 0) pz = 0;
if (pz > 1) pz = 1;
px *= p->Domain[0];
py *= p->Domain[1];
pz *= p->Domain[2];
px = fclamp(Input[0]) * p->Domain[0];
py = fclamp(Input[1]) * p->Domain[1];
pz = fclamp(Input[2]) * p->Domain[2];
x0 = (int) _cmsQuickFloor(px); rx = (px - (cmsFloat32Number) x0);
y0 = (int) _cmsQuickFloor(py); ry = (py - (cmsFloat32Number) y0);
@ -675,7 +688,6 @@ void TetrahedralInterpFloat(const cmsFloat32Number Input[],
#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
static
void TetrahedralInterp16(register const cmsUInt16Number Input[],
@ -683,99 +695,131 @@ void TetrahedralInterp16(register const cmsUInt16Number Input[],
register const cmsInterpParams* p)
{
const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table;
cmsS15Fixed16Number fx, fy, fz;
cmsS15Fixed16Number rx, ry, rz;
int x0, y0, z0;
cmsS15Fixed16Number c0, c1, c2, c3, Rest;
cmsUInt32Number OutChan;
cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
cmsUInt32Number TotalOut = p -> nOutputs;
cmsS15Fixed16Number fx, fy, fz;
cmsS15Fixed16Number rx, ry, rz;
int x0, y0, z0;
cmsS15Fixed16Number c0, c1, c2, c3, Rest;
cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
cmsUInt32Number TotalOut = p -> nOutputs;
fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]);
fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]);
fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]);
fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]);
fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]);
fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]);
x0 = FIXED_TO_INT(fx);
y0 = FIXED_TO_INT(fy);
z0 = FIXED_TO_INT(fz);
x0 = FIXED_TO_INT(fx);
y0 = FIXED_TO_INT(fy);
z0 = FIXED_TO_INT(fz);
rx = FIXED_REST_TO_INT(fx);
ry = FIXED_REST_TO_INT(fy);
rz = FIXED_REST_TO_INT(fz);
rx = FIXED_REST_TO_INT(fx);
ry = FIXED_REST_TO_INT(fy);
rz = FIXED_REST_TO_INT(fz);
X0 = p -> opta[2] * x0;
X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]);
X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]);
Y0 = p -> opta[1] * y0;
Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]);
Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]);
Z0 = p -> opta[0] * z0;
Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]);
Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]);
// These are the 6 Tetrahedral
for (OutChan=0; OutChan < TotalOut; OutChan++) {
LutTable = &LutTable[X0+Y0+Z0];
c0 = DENS(X0, Y0, Z0);
if (rx >= ry && ry >= rz) {
c1 = DENS(X1, Y0, Z0) - c0;
c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);
c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
}
else
if (rx >= rz && rz >= ry) {
c1 = DENS(X1, Y0, Z0) - c0;
c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);
// Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))
// which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16
// This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16
// at the cost of being off by one at 7fff and 17ffe.
if (rx >= ry) {
if (ry >= rz) {
Y1 += X1;
Z1 += Y1;
for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c2 = LutTable[Y1];
c3 = LutTable[Z1];
c0 = *LutTable++;
c3 -= c2;
c2 -= c1;
c1 -= c0;
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
}
else
if (rz >= rx && rx >= ry) {
c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);
c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
c3 = DENS(X0, Y0, Z1) - c0;
}
else
if (ry >= rx && rx >= rz) {
c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);
c2 = DENS(X0, Y1, Z0) - c0;
c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
}
else
if (ry >= rz && rz >= rx) {
c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
c2 = DENS(X0, Y1, Z0) - c0;
c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);
}
else
if (rz >= ry && ry >= rx) {
c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);
c3 = DENS(X0, Y0, Z1) - c0;
}
else {
c1 = c2 = c3 = 0;
}
Rest = c1 * rx + c2 * ry + c3 * rz;
Output[OutChan] = (cmsUInt16Number) c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest));
} else if (rz >= rx) {
X1 += Z1;
Y1 += X1;
for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c2 = LutTable[Y1];
c3 = LutTable[Z1];
c0 = *LutTable++;
c2 -= c1;
c1 -= c3;
c3 -= c0;
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
}
} else {
Z1 += X1;
Y1 += Z1;
for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c2 = LutTable[Y1];
c3 = LutTable[Z1];
c0 = *LutTable++;
c2 -= c3;
c3 -= c1;
c1 -= c0;
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
}
}
} else {
if (rx >= rz) {
X1 += Y1;
Z1 += X1;
for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c2 = LutTable[Y1];
c3 = LutTable[Z1];
c0 = *LutTable++;
c3 -= c1;
c1 -= c2;
c2 -= c0;
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
}
} else if (ry >= rz) {
Z1 += Y1;
X1 += Z1;
for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c2 = LutTable[Y1];
c3 = LutTable[Z1];
c0 = *LutTable++;
c1 -= c3;
c3 -= c2;
c2 -= c0;
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
}
} else {
Y1 += Z1;
X1 += Y1;
for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c2 = LutTable[Y1];
c3 = LutTable[Z1];
c0 = *LutTable++;
c1 -= c2;
c2 -= c3;
c3 -= c0;
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
}
}
}
}
#undef DENS
#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
@ -784,7 +828,7 @@ void Eval4Inputs(register const cmsUInt16Number Input[],
register cmsUInt16Number Output[],
register const cmsInterpParams* p16)
{
const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
const cmsUInt16Number* LutTable;
cmsS15Fixed16Number fk;
cmsS15Fixed16Number k0, rk;
int K0, K1;
@ -979,8 +1023,7 @@ void Eval4InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1;
pk = Input[0] * p->Domain[0];
pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0;
@ -1067,7 +1110,7 @@ void Eval5InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1;
pk = Input[0] * p->Domain[0];
pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0;
@ -1154,7 +1197,7 @@ void Eval6InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1;
pk = Input[0] * p->Domain[0];
pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0;
@ -1207,7 +1250,7 @@ void Eval7Inputs(register const cmsUInt16Number Input[],
K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));
p1 = *p16;
memmove(&p1.Domain[0], &p16 ->Domain[1], 5*sizeof(cmsUInt32Number));
memmove(&p1.Domain[0], &p16 ->Domain[1], 6*sizeof(cmsUInt32Number));
T = LutTable + K0;
p1.Table = T;
@ -1239,7 +1282,7 @@ void Eval7InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1;
pk = Input[0] * p->Domain[0];
pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0;
@ -1324,7 +1367,7 @@ void Eval8InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1;
pk = Input[0] * p->Domain[0];
pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -100,7 +100,6 @@ cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile)
Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag);
if (Tag != NULL) {
*Dest = *Tag;
return TRUE;
}
@ -121,7 +120,7 @@ cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile)
return TRUE;
}
return _cmsAdaptationMatrix(Dest, NULL, cmsD50_XYZ(), White);
return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ());
}
}
@ -164,7 +163,8 @@ cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
if (GrayTRC == NULL) return NULL;
Lut = cmsPipelineAlloc(ContextID, 1, 3);
if (Lut == NULL) return NULL;
if (Lut == NULL)
goto Error;
if (cmsGetPCS(hProfile) == cmsSigLabData) {
@ -175,28 +175,35 @@ cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
if (EmptyTab == NULL) {
cmsPipelineFree(Lut);
return NULL;
}
if (EmptyTab == NULL)
goto Error;
LabCurves[0] = GrayTRC;
LabCurves[1] = EmptyTab;
LabCurves[2] = EmptyTab;
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL));
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves));
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) ||
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) {
cmsFreeToneCurve(EmptyTab);
goto Error;
}
cmsFreeToneCurve(EmptyTab);
}
else {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC));
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL));
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) ||
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL)))
goto Error;
}
return Lut;
Error:
cmsFreeToneCurve(GrayTRC);
cmsPipelineFree(Lut);
return NULL;
}
// RGB Matrix shaper
@ -230,15 +237,76 @@ cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile)
Lut = cmsPipelineAlloc(ContextID, 3, 3);
if (Lut != NULL) {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes));
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL));
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) ||
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL)))
goto Error;
// Note that it is certainly possible a single profile would have a LUT based
// tag for output working in lab and a matrix-shaper for the fallback cases.
// This is not allowed by the spec, but this code is tolerant to those cases
if (cmsGetPCS(hProfile) == cmsSigLabData) {
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID)))
goto Error;
}
}
return Lut;
Error:
cmsPipelineFree(Lut);
return NULL;
}
// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
static
cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
{
cmsContext ContextID = cmsGetProfileContextID(hProfile);
cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
if (Lut == NULL) return NULL;
// input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used,
// these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0)
if ( spc == cmsSigLabData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
goto Error;
}
else if (spc == cmsSigXYZData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
goto Error;
}
if ( PCS == cmsSigLabData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
goto Error;
}
else if( PCS == cmsSigXYZData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
goto Error;
}
return Lut;
Error:
cmsPipelineFree(Lut);
return NULL;
}
// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
// is adjusted here in order to create a LUT that takes care of all those details
// is adjusted here in order to create a LUT that takes care of all those details.
// We add intent = -1 as a way to read matrix shaper always, no matter of other LUT
cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
{
cmsTagTypeSignature OriginalType;
@ -246,40 +314,78 @@ cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
cmsTagSignature tagFloat = Device2PCSFloat[Intent];
cmsContext ContextID = cmsGetProfileContextID(hProfile);
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
// On named color, take the appropiate tag
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
// Floating point LUT are always V4, so no adjustment is required
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
}
cmsPipeline* Lut;
cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
// Revert to perceptual if no tag is found
if (!cmsIsTag(hProfile, tag16)) {
tag16 = Device2PCS16[0];
}
if (nc == NULL) return NULL;
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
Lut = cmsPipelineAlloc(ContextID, 0, 0);
if (Lut == NULL) {
cmsFreeNamedColorList(nc);
return NULL;
}
// Check profile version and LUT type. Do the necessary adjustments if needed
// First read the tag
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
if (Lut == NULL) return NULL;
// After reading it, we have now info about the original type
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
// The profile owns the Lut, so we need to copy it
Lut = cmsPipelineDup(Lut);
// We need to adjust data only for Lab16 on output
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
return Lut;
// Add a matrix for conversion V2 to V4 Lab PCS
cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID));
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
cmsPipelineFree(Lut);
return NULL;
}
return Lut;
}
// This is an attempt to reuse this funtion to retrieve the matrix-shaper as pipeline no
// matter other LUT are present and have precedence. Intent = -1 means just this.
if (Intent != -1) {
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
// Floating point LUT are always V4, but the encoding range is no
// longer 0..1.0, so we need to add an stage depending on the color space
return _cmsReadFloatInputTag(hProfile, tagFloat);
}
// Revert to perceptual if no tag is found
if (!cmsIsTag(hProfile, tag16)) {
tag16 = Device2PCS16[0];
}
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
// Check profile version and LUT type. Do the necessary adjustments if needed
// First read the tag
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
if (Lut == NULL) return NULL;
// After reading it, we have now info about the original type
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
// The profile owns the Lut, so we need to copy it
Lut = cmsPipelineDup(Lut);
// We need to adjust data only for Lab16 on output
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
return Lut;
// If the input is Lab, add also a conversion at the begin
if (cmsGetColorSpace(hProfile) == cmsSigLabData &&
!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
goto Error;
// Add a matrix for conversion V2 to V4 Lab PCS
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
goto Error;
return Lut;
Error:
cmsPipelineFree(Lut);
return NULL;
}
}
// Lut was not found, try to create a matrix-shaper
// Check if this is a grayscale profile.
@ -322,21 +428,27 @@ cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
if (cmsGetPCS(hProfile) == cmsSigLabData) {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL));
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
goto Error;
}
else {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL));
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL)))
goto Error;
}
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC));
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC)))
goto Error;
cmsFreeToneCurve(RevGrayTRC);
return Lut;
Error:
cmsFreeToneCurve(RevGrayTRC);
cmsPipelineFree(Lut);
return NULL;
}
static
cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
{
@ -378,12 +490,26 @@ cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
Lut = cmsPipelineAlloc(ContextID, 3, 3);
if (Lut != NULL) {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL));
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes));
// Note that it is certainly possible a single profile would have a LUT based
// tag for output working in lab and a matrix-shaper for the fallback cases.
// This is not allowed by the spec, but this code is tolerant to those cases
if (cmsGetPCS(hProfile) == cmsSigLabData) {
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID)))
goto Error;
}
if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) ||
!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes)))
goto Error;
}
cmsFreeToneCurveTriple(InvShapes);
return Lut;
Error:
cmsFreeToneCurveTriple(InvShapes);
cmsPipelineFree(Lut);
return NULL;
}
@ -402,11 +528,56 @@ void ChangeInterpolationToTrilinear(cmsPipeline* Lut)
_cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data;
CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR;
_cmsSetInterpolationRoutine(CLUT ->Params);
_cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params);
}
}
}
// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
static
cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
{
cmsContext ContextID = cmsGetProfileContextID(hProfile);
cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile);
if (Lut == NULL) return NULL;
// If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding,
// and since the formatter has already accomodated to 0..1.0, we should undo this change
if ( PCS == cmsSigLabData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
goto Error;
}
else
if (PCS == cmsSigXYZData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
goto Error;
}
// the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline
if ( dataSpace == cmsSigLabData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
goto Error;
}
else if (dataSpace == cmsSigXYZData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
goto Error;
}
return Lut;
Error:
cmsPipelineFree(Lut);
return NULL;
}
// Create an output MPE LUT from agiven profile. Version mismatches are handled here
cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
{
@ -415,62 +586,117 @@ cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
cmsTagSignature tagFloat = PCS2DeviceFloat[Intent];
cmsContext ContextID = cmsGetProfileContextID(hProfile);
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
// Floating point LUT are always V4, so no adjustment is required
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
}
if (Intent != -1) {
// Revert to perceptual if no tag is found
if (!cmsIsTag(hProfile, tag16)) {
tag16 = PCS2Device16[0];
}
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
// Floating point LUT are always V4
return _cmsReadFloatOutputTag(hProfile, tagFloat);
}
// Check profile version and LUT type. Do the necessary adjustments if needed
// Revert to perceptual if no tag is found
if (!cmsIsTag(hProfile, tag16)) {
tag16 = PCS2Device16[0];
}
// First read the tag
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
if (Lut == NULL) return NULL;
if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
// After reading it, we have info about the original type
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
// Check profile version and LUT type. Do the necessary adjustments if needed
// The profile owns the Lut, so we need to copy it
Lut = cmsPipelineDup(Lut);
if (Lut == NULL) return NULL;
// First read the tag
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
if (Lut == NULL) return NULL;
// Now it is time for a controversial stuff. I found that for 3D LUTS using
// Lab used as indexer space, trilinear interpolation should be used
if (cmsGetPCS(hProfile) == cmsSigLabData)
ChangeInterpolationToTrilinear(Lut);
// After reading it, we have info about the original type
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
// The profile owns the Lut, so we need to copy it
Lut = cmsPipelineDup(Lut);
if (Lut == NULL) return NULL;
// Now it is time for a controversial stuff. I found that for 3D LUTS using
// Lab used as indexer space, trilinear interpolation should be used
if (cmsGetPCS(hProfile) == cmsSigLabData)
ChangeInterpolationToTrilinear(Lut);
// We need to adjust data only for Lab and Lut16 type
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
return Lut;
// Add a matrix for conversion V4 to V2 Lab PCS
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
goto Error;
// If the output is Lab, add also a conversion at the end
if (cmsGetColorSpace(hProfile) == cmsSigLabData)
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
goto Error;
// We need to adjust data only for Lab and Lut16 type
if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData)
return Lut;
// Add a matrix for conversion V4 to V2 Lab PCS
cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID));
return Lut;
Error:
cmsPipelineFree(Lut);
return NULL;
}
}
// Lut not found, try to create a matrix-shaper
// Check if this is a grayscale profile.
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
// if so, build appropiate conversion tables.
// The tables are the PCS iluminant, scaled across GrayTRC
return BuildGrayOutputPipeline(hProfile);
// if so, build appropiate conversion tables.
// The tables are the PCS iluminant, scaled across GrayTRC
return BuildGrayOutputPipeline(hProfile);
}
// Not gray, create a normal matrix-shaper
// Not gray, create a normal matrix-shaper, which only operates in XYZ space
return BuildRGBOutputMatrixShaper(hProfile);
}
// ---------------------------------------------------------------------------------------------------------------
// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
static
cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
{
cmsContext ContextID = cmsGetProfileContextID(hProfile);
cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
if (Lut == NULL) return NULL;
if (spc == cmsSigLabData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
goto Error;
}
else
if (spc == cmsSigXYZData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
goto Error;
}
if (PCS == cmsSigLabData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
goto Error;
}
else
if (PCS == cmsSigXYZData)
{
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
goto Error;
}
return Lut;
Error:
cmsPipelineFree(Lut);
return NULL;
}
// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
// tag name here may default to AToB0
cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
@ -481,10 +707,36 @@ cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
cmsTagSignature tagFloat = Device2PCSFloat[Intent];
cmsContext ContextID = cmsGetProfileContextID(hProfile);
// On named color, take the appropiate tag
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
if (nc == NULL) return NULL;
Lut = cmsPipelineAlloc(ContextID, 0, 0);
if (Lut == NULL)
goto Error;
if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE)))
goto Error;
if (cmsGetColorSpace(hProfile) == cmsSigLabData)
if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
goto Error;
return Lut;
Error:
cmsPipelineFree(Lut);
cmsFreeNamedColorList(nc);
return NULL;
}
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
// Floating point LUT are always V4, no adjustment is required
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
// Floating point LUT are always V
return _cmsReadFloatDevicelinkTag(hProfile, tagFloat);
}
tagFloat = Device2PCSFloat[0];
@ -509,10 +761,10 @@ cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
Lut = cmsPipelineDup(Lut);
if (Lut == NULL) return NULL;
// Now it is time for a controversial stuff. I found that for 3D LUTS using
// Lab used as indexer space, trilinear interpolation should be used
if (cmsGetColorSpace(hProfile) == cmsSigLabData)
ChangeInterpolationToTrilinear(Lut);
// Now it is time for a controversial stuff. I found that for 3D LUTS using
// Lab used as indexer space, trilinear interpolation should be used
if (cmsGetPCS(hProfile) == cmsSigLabData)
ChangeInterpolationToTrilinear(Lut);
// After reading it, we have info about the original type
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
@ -522,17 +774,21 @@ cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
// Here it is possible to get Lab on both sides
if (cmsGetPCS(hProfile) == cmsSigLabData) {
cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID));
if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
if(!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
goto Error2;
}
if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID));
if (cmsGetPCS(hProfile) == cmsSigLabData) {
if(!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
goto Error2;
}
return Lut;
Error2:
cmsPipelineFree(Lut);
return NULL;
}
// ---------------------------------------------------------------------------------------------------------------
@ -611,7 +867,6 @@ cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
// Read both, profile sequence description and profile sequence id if present. Then combine both to
// create qa unique structure holding both. Shame on ICC to store things in such complicated way.
cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
{
cmsSEQ* ProfileSeq;
@ -636,12 +891,13 @@ cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
NewSeq = cmsDupProfileSequenceDescription(ProfileSeq);
// Ok, proceed to the mixing
for (i=0; i < ProfileSeq ->n; i++) {
if (NewSeq != NULL) {
for (i=0; i < ProfileSeq ->n; i++) {
memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID));
NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID));
NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
}
}
return NewSeq;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -125,7 +125,7 @@ cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cms
for (i=0; i < n; i++) {
// Get asked type
Type = va_arg(args, cmsStageSignature);
Type = (cmsStageSignature)va_arg(args, cmsStageSignature);
if (mpe ->Type != Type) {
va_end(args); // Mismatch. We are done.
@ -235,10 +235,10 @@ Error:
if (NewElem ->TheCurves != NULL) {
for (i=0; i < NewElem ->nCurves; i++) {
if (NewElem ->TheCurves[i])
cmsFreeToneCurve(Data ->TheCurves[i]);
cmsFreeToneCurve(NewElem ->TheCurves[i]);
}
}
_cmsFree(mpe ->ContextID, Data ->TheCurves);
_cmsFree(mpe ->ContextID, NewElem ->TheCurves);
_cmsFree(mpe ->ContextID, NewElem);
return NULL;
}
@ -256,7 +256,7 @@ cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Numbe
EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL );
if (NewMPE == NULL) return NULL;
NewElem = (_cmsStageToneCurvesData*) _cmsMalloc(ContextID, sizeof(_cmsStageToneCurvesData));
NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData));
if (NewElem == NULL) {
cmsStageFree(NewMPE);
return NULL;
@ -284,9 +284,10 @@ cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Numbe
cmsStageFree(NewMPE);
return NULL;
}
}
return NewMPE;
return NewMPE;
}
@ -362,6 +363,8 @@ static
void MatrixElemTypeFree(cmsStage* mpe)
{
_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
if (Data == NULL)
return;
if (Data ->Double)
_cmsFree(mpe ->ContextID, Data ->Double);
@ -496,10 +499,15 @@ void* CLUTElemDup(cmsStage* mpe)
if (Data ->Tab.T) {
if (Data ->HasFloatValues)
if (Data ->HasFloatValues) {
NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number));
else
if (NewElem ->Tab.TFloat == NULL)
goto Error;
} else {
NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number));
if (NewElem ->Tab.TFloat == NULL)
goto Error;
}
}
NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID,
@ -508,8 +516,14 @@ void* CLUTElemDup(cmsStage* mpe)
Data ->Params ->nOutputs,
NewElem ->Tab.T,
Data ->Params ->dwFlags);
return (void*) NewElem;
if (NewElem->Params != NULL)
return (void*) NewElem;
Error:
if (NewElem->Tab.T)
// This works for both types
_cmsFree(mpe ->ContextID, NewElem -> Tab.T);
_cmsFree(mpe ->ContextID, NewElem);
return NULL;
}
@ -543,12 +557,19 @@ cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID,
_cmsStageCLutData* NewElem;
cmsStage* NewMPE;
_cmsAssert(clutPoints != NULL);
if (inputChan > MAX_INPUT_DIMENSIONS) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);
return NULL;
}
NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL );
if (NewMPE == NULL) return NULL;
NewElem = (_cmsStageCLutData*) _cmsMalloc(ContextID, sizeof(_cmsStageCLutData));
NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
if (NewElem == NULL) {
cmsStageFree(NewMPE);
return NULL;
@ -599,7 +620,6 @@ cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID,
for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
Dimensions[i] = nGridPoints;
return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table);
}
@ -630,12 +650,17 @@ cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const c
_cmsAssert(clutPoints != NULL);
if (inputChan > MAX_INPUT_DIMENSIONS) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);
return NULL;
}
NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL);
if (NewMPE == NULL) return NULL;
NewElem = (_cmsStageCLutData*) _cmsMalloc(ContextID, sizeof(_cmsStageCLutData));
NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
if (NewElem == NULL) {
cmsStageFree(NewMPE);
return NULL;
@ -644,7 +669,7 @@ cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const c
NewMPE ->Data = (void*) NewElem;
// There is a potential integer overflow on conputing n and nEntries.
NewElem -> nEntries = n = outputChan * CubeSize( clutPoints, inputChan);
NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);
NewElem -> HasFloatValues = TRUE;
if (n == 0) {
@ -664,16 +689,12 @@ cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const c
}
}
NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT);
if (NewElem ->Params == NULL) {
cmsStageFree(NewMPE);
return NULL;
}
return NewMPE;
}
@ -731,15 +752,22 @@ cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, v
int i, t, nTotalPoints, index, rest;
int nInputs, nOutputs;
cmsUInt32Number* nSamples;
cmsUInt16Number In[cmsMAXCHANNELS], Out[MAX_STAGE_CHANNELS];
_cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;
cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
_cmsStageCLutData* clut;
if (mpe == NULL) return FALSE;
clut = (_cmsStageCLutData*) mpe->Data;
if (clut == NULL) return FALSE;
nSamples = clut->Params ->nSamples;
nInputs = clut->Params ->nInputs;
nOutputs = clut->Params ->nOutputs;
if (nInputs >= cmsMAXCHANNELS) return FALSE;
if (nInputs <= 0) return FALSE;
if (nOutputs <= 0) return FALSE;
if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;
if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
nTotalPoints = CubeSize(nSamples, nInputs);
@ -786,14 +814,16 @@ cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler
int i, t, nTotalPoints, index, rest;
int nInputs, nOutputs;
cmsUInt32Number* nSamples;
cmsFloat32Number In[cmsMAXCHANNELS], Out[MAX_STAGE_CHANNELS];
cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
_cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;
nSamples = clut->Params ->nSamples;
nInputs = clut->Params ->nInputs;
nOutputs = clut->Params ->nOutputs;
if (nInputs >= cmsMAXCHANNELS) return FALSE;
if (nInputs <= 0) return FALSE;
if (nOutputs <= 0) return FALSE;
if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;
if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
nTotalPoints = CubeSize(nSamples, nInputs);
@ -975,6 +1005,7 @@ cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID)
mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable);
cmsFreeToneCurveTriple(LabTable);
if (mpe == NULL) return NULL;
mpe ->Implements = cmsSigLabV2toV4;
return mpe;
}
@ -1013,6 +1044,89 @@ cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID)
}
// To Lab to float. Note that the MPE gives numbers in normal Lab range
// and we need 0..1.0 range for the formatters
// L* : 0...100 => 0...1.0 (L* / 100)
// ab* : -128..+127 to 0..1 ((ab* + 128) / 255)
cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID)
{
static const cmsFloat64Number a1[] = {
1.0/100.0, 0, 0,
0, 1.0/255.0, 0,
0, 0, 1.0/255.0
};
static const cmsFloat64Number o1[] = {
0,
128.0/255.0,
128.0/255.0
};
cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);
if (mpe == NULL) return mpe;
mpe ->Implements = cmsSigLab2FloatPCS;
return mpe;
}
// Fom XYZ to floating point PCS
cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID)
{
#define n (32768.0/65535.0)
static const cmsFloat64Number a1[] = {
n, 0, 0,
0, n, 0,
0, 0, n
};
#undef n
cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);
if (mpe == NULL) return mpe;
mpe ->Implements = cmsSigXYZ2FloatPCS;
return mpe;
}
cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID)
{
static const cmsFloat64Number a1[] = {
100.0, 0, 0,
0, 255.0, 0,
0, 0, 255.0
};
static const cmsFloat64Number o1[] = {
0,
-128.0,
-128.0
};
cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);
if (mpe == NULL) return mpe;
mpe ->Implements = cmsSigFloatPCS2Lab;
return mpe;
}
cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID)
{
#define n (65535.0/32768.0)
static const cmsFloat64Number a1[] = {
n, 0, 0,
0, n, 0,
0, 0, n
};
#undef n
cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);
if (mpe == NULL) return mpe;
mpe ->Implements = cmsSigFloatPCS2XYZ;
return mpe;
}
// ********************************************************************************
// Type cmsSigXYZ2LabElemType
// ********************************************************************************
@ -1117,12 +1231,22 @@ cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe)
NULL);
if (NewMPE == NULL) return NULL;
NewMPE ->Implements = mpe ->Implements;
NewMPE ->Implements = mpe ->Implements;
if (mpe ->DupElemPtr) {
NewMPE ->Data = mpe ->DupElemPtr(mpe);
if (NewMPE->Data == NULL) {
cmsStageFree(NewMPE);
return NULL;
}
} else {
if (mpe ->DupElemPtr)
NewMPE ->Data = mpe ->DupElemPtr(mpe);
else
NewMPE ->Data = NULL;
}
return NewMPE;
}
@ -1229,14 +1353,21 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In
return NewLUT;
}
cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut)
{
_cmsAssert(lut != NULL);
return lut ->ContextID;
}
cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut)
{
_cmsAssert(lut != NULL);
return lut ->InputChannels;
}
cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut)
{
_cmsAssert(lut != NULL);
return lut ->OutputChannels;
}
@ -1264,6 +1395,7 @@ void CMSEXPORT cmsPipelineFree(cmsPipeline* lut)
// Default to evaluate the LUT on 16 bit-basis.
void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut)
{
_cmsAssert(lut != NULL);
lut ->Eval16Fn(In, Out, lut->Data);
}
@ -1271,6 +1403,7 @@ void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out
// Does evaluate the LUT on cmsFloat32Number-basis.
void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut)
{
_cmsAssert(lut != NULL);
lut ->EvalFloatFn(In, Out, lut);
}
@ -1286,6 +1419,8 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
if (lut == NULL) return NULL;
NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels);
if (NewLUT == NULL) return NULL;
for (mpe = lut ->Elements;
mpe != NULL;
mpe = mpe ->Next) {
@ -1308,8 +1443,10 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
Anterior = NewMPE;
}
NewLUT ->DupDataFn = lut ->DupDataFn;
NewLUT ->FreeDataFn = lut ->FreeDataFn;
NewLUT ->Eval16Fn = lut ->Eval16Fn;
NewLUT ->EvalFloatFn = lut ->EvalFloatFn;
NewLUT ->DupDataFn = lut ->DupDataFn;
NewLUT ->FreeDataFn = lut ->FreeDataFn;
if (NewLUT ->DupDataFn != NULL)
NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data);
@ -1322,12 +1459,12 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
}
void CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe)
int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe)
{
cmsStage* Anterior = NULL, *pt;
_cmsAssert(lut != NULL);
_cmsAssert(mpe != NULL);
if (lut == NULL || mpe == NULL)
return FALSE;
switch (loc) {
@ -1351,9 +1488,11 @@ void CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStag
}
break;
default:;
return FALSE;
}
BlessLUT(lut);
return TRUE;
}
// Unlink an element and return the pointer to it
@ -1415,7 +1554,7 @@ void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStag
// Concatenate two LUT into a new single one
cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)
{
cmsStage* mpe, *NewMPE;
cmsStage* mpe;
// If both LUTS does not have elements, we need to inherit
// the number of channels
@ -1430,17 +1569,12 @@ cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)
mpe = mpe ->Next) {
// We have to dup each element
NewMPE = cmsStageDup(mpe);
if (NewMPE == NULL) {
return FALSE;
}
cmsPipelineInsertStage(l1, cmsAT_END, NewMPE);
if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe)))
return FALSE;
}
BlessLUT(l1);
return TRUE;
BlessLUT(l1);
return TRUE;
}
@ -1479,13 +1613,13 @@ cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut)
return n;
}
// This function may be used to set the optional evalueator and a block of private data. If private data is being used, an optional
// 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.
void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
_cmsOPTeval16Fn Eval16,
void* PrivateData,
_cmsOPTfreeDataFn FreePrivateDataFn,
_cmsOPTdupDataFn DupPrivateDataFn)
_cmsFreeUserDataFn FreePrivateDataFn,
_cmsDupUserDataFn DupPrivateDataFn)
{
Lut ->Eval16Fn = Eval16;
@ -1570,16 +1704,11 @@ cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[],
cmsFloat32Number fx[4], x[4], xd[4], fxd[4];
cmsVEC3 tmp, tmp2;
cmsMAT3 Jacobian;
cmsFloat64Number LastResult[4];
// Only 3->3 and 4->3 are supported
if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE;
if (lut ->OutputChannels != 3) return FALSE;
// Mark result of -1
LastResult[0] = LastResult[1] = LastResult[2] = -1.0f;
// Take the hint as starting point if specified
if (Hint == NULL) {
@ -1663,3 +1792,4 @@ cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[],
return TRUE;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -267,7 +267,7 @@ cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile)
_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
_cmsICCPROFILE Keep;
_cmsAssert(hProfile != NULL);
_cmsAssert(hProfile != NULL);
ContextID = cmsGetProfileContextID(hProfile);
@ -309,7 +309,7 @@ cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile)
Error:
// Free resources as something went wrong
if (MD5 != NULL) _cmsFree(ContextID, MD5);
// "MD5" cannot be other than NULL here, so no need to free it
if (Mem != NULL) _cmsFree(ContextID, Mem);
memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
return FALSE;

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -97,16 +97,16 @@ cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b)
cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a)
{
cmsMAT3 Identity;
int i, j;
cmsMAT3 Identity;
int i, j;
_cmsMAT3identity(&Identity);
_cmsMAT3identity(&Identity);
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;
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;
return TRUE;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2012 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"),
@ -32,29 +32,29 @@
// Allocates an empty multi localizad unicode object
cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
{
cmsMLU* mlu;
cmsMLU* mlu;
// nItems should be positive if given
if (nItems <= 0) nItems = 2;
// nItems should be positive if given
if (nItems <= 0) nItems = 2;
// Create the container
mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
if (mlu == NULL) return NULL;
// Create the container
mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
if (mlu == NULL) return NULL;
mlu ->ContextID = ContextID;
mlu ->ContextID = ContextID;
// Create entry array
mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
if (mlu ->Entries == NULL) {
_cmsFree(ContextID, mlu);
return NULL;
}
// Create entry array
mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
if (mlu ->Entries == NULL) {
_cmsFree(ContextID, mlu);
return NULL;
}
// Ok, keep indexes up to date
mlu ->AllocatedEntries = nItems;
mlu ->UsedEntries = 0;
// Ok, keep indexes up to date
mlu ->AllocatedEntries = nItems;
mlu ->UsedEntries = 0;
return mlu;
return mlu;
}
@ -62,48 +62,48 @@ cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
static
cmsBool GrowMLUpool(cmsMLU* mlu)
{
cmsUInt32Number size;
void *NewPtr;
cmsUInt32Number size;
void *NewPtr;
// Sanity check
if (mlu == NULL) return FALSE;
// Sanity check
if (mlu == NULL) return FALSE;
if (mlu ->PoolSize == 0)
size = 256;
else
size = mlu ->PoolSize * 2;
if (mlu ->PoolSize == 0)
size = 256;
else
size = mlu ->PoolSize * 2;
// Check for overflow
if (size < mlu ->PoolSize) return FALSE;
// Check for overflow
if (size < mlu ->PoolSize) return FALSE;
// Reallocate the pool
NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
if (NewPtr == NULL) return FALSE;
// Reallocate the pool
NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
if (NewPtr == NULL) return FALSE;
mlu ->MemPool = NewPtr;
mlu ->PoolSize = size;
mlu ->MemPool = NewPtr;
mlu ->PoolSize = size;
return TRUE;
return TRUE;
}
// Grows a ntry table for a MLU. Each time this function is called, table size is multiplied times two.
// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.
static
cmsBool GrowMLUtable(cmsMLU* mlu)
{
int AllocatedEntries;
_cmsMLUentry *NewPtr;
// Sanity check
if (mlu == NULL) return FALSE;
// Sanity check
if (mlu == NULL) return FALSE;
AllocatedEntries = mlu ->AllocatedEntries * 2;
// Check for overflow
if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
// Check for overflow
if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
// Reallocate the memory
// Reallocate the memory
NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
if (NewPtr == NULL) return FALSE;
@ -120,17 +120,17 @@ int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number Co
{
int i;
// Sanity check
if (mlu == NULL) return -1;
// Sanity check
if (mlu == NULL) return -1;
// Iterate whole table
// Iterate whole table
for (i=0; i < mlu ->UsedEntries; i++) {
if (mlu ->Entries[i].Country == CountryCode &&
mlu ->Entries[i].Language == LanguageCode) return i;
}
// Not found
// Not found
return -1;
}
@ -143,8 +143,8 @@ cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
cmsUInt32Number Offset;
cmsUInt8Number* Ptr;
// Sanity check
if (mlu == NULL) return FALSE;
// Sanity check
if (mlu == NULL) return FALSE;
// Is there any room available?
if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
@ -155,17 +155,17 @@ cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed!
// Check for size
while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
if (!GrowMLUpool(mlu)) return FALSE;
}
}
Offset = mlu ->PoolUsed;
Ptr = (cmsUInt8Number*) mlu ->MemPool;
if (Ptr == NULL) return FALSE;
Ptr = (cmsUInt8Number*) mlu ->MemPool;
if (Ptr == NULL) return FALSE;
// Set the entry
// Set the entry
memmove(Ptr + Offset, Block, size);
mlu ->PoolUsed += size;
@ -209,7 +209,7 @@ cmsUInt32Number mywcslen(const wchar_t *s)
{
const wchar_t *p;
p = s;
p = s;
while (*p)
p++;
@ -225,7 +225,7 @@ cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char
cmsUInt32Number len;
if (mlu == NULL) return FALSE;
if (WideString == NULL) return FALSE;
if (WideString == NULL) return FALSE;
len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t);
return AddMLUBlock(mlu, len, WideString, Lang, Cntry);
@ -234,59 +234,59 @@ cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char
// Duplicating a MLU is as easy as copying all members
cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)
{
cmsMLU* NewMlu = NULL;
cmsMLU* NewMlu = NULL;
// Duplicating a NULL obtains a NULL
if (mlu == NULL) return NULL;
// Duplicating a NULL obtains a NULL
if (mlu == NULL) return NULL;
NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
if (NewMlu == NULL) return NULL;
NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
if (NewMlu == NULL) return NULL;
// Should never happen
if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
goto Error;
// Should never happen
if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
goto Error;
// Sanitize...
if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) 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;
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;
}
// 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;
NewMlu ->PoolSize = mlu ->PoolUsed;
if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
NewMlu ->PoolUsed = mlu ->PoolUsed;
memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
NewMlu ->PoolUsed = mlu ->PoolUsed;
return NewMlu;
return NewMlu;
Error:
if (NewMlu != NULL) cmsMLUfree(NewMlu);
return NULL;
if (NewMlu != NULL) cmsMLUfree(NewMlu);
return NULL;
}
// Free any used memory
void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
{
if (mlu) {
if (mlu) {
if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);
if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);
_cmsFree(mlu ->ContextID, mlu);
}
_cmsFree(mlu ->ContextID, mlu);
}
}
@ -294,13 +294,13 @@ void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
// 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)
cmsUInt32Number *len,
cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
{
int i;
int Best = -1;
_cmsMLUentry* v;
_cmsMLUentry* v;
if (mlu == NULL) return NULL;
@ -316,8 +316,8 @@ const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
if (v -> Country == CountryCode) {
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
if (len != NULL) *len = v ->Len;
@ -330,30 +330,30 @@ const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
if (Best == -1)
Best = 0;
v = mlu ->Entries + Best;
v = mlu ->Entries + Best;
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
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);
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 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 Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
// Sanitize
// Sanitize
if (mlu == NULL) return 0;
// Get WideChar
@ -381,23 +381,23 @@ cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
Buffer[i] = (char) Wide[i];
}
// We put a termination "\0"
// 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 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 Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
// Sanitize
// Sanitize
if (mlu == NULL) return 0;
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
@ -414,7 +414,7 @@ cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
StrLen = BufferSize - + sizeof(wchar_t);
memmove(Buffer, Wide, StrLen);
Buffer[StrLen / sizeof(wchar_t)] = 0;
Buffer[StrLen / sizeof(wchar_t)] = 0;
return StrLen + sizeof(wchar_t);
}
@ -422,27 +422,56 @@ cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
// 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 char LanguageCode[3], const char CountryCode[3],
char ObtainedLanguage[3], char ObtainedCountry[3])
{
const wchar_t *Wide;
const wchar_t *Wide;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
cmsUInt16Number ObtLang, ObtCode;
// Sanitize
// Sanitize
if (mlu == NULL) return FALSE;
Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
if (Wide == NULL) return FALSE;
if (Wide == NULL) return FALSE;
// Get used language and code
// Get used language and code
*(cmsUInt16Number *)ObtainedLanguage = _cmsAdjustEndianess16(ObtLang);
*(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode);
*(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode);
ObtainedLanguage[2] = ObtainedCountry[2] = 0;
return TRUE;
ObtainedLanguage[2] = ObtainedCountry[2] = 0;
return TRUE;
}
// Get the number of translations in the MLU object
cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu)
{
if (mlu == NULL) return 0;
return mlu->UsedEntries;
}
// Get the language and country codes for a specific MLU index
cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu,
cmsUInt32Number idx,
char LanguageCode[3],
char CountryCode[3])
{
_cmsMLUentry *entry;
if (mlu == NULL) return FALSE;
if (idx >= (cmsUInt32Number) mlu->UsedEntries) return FALSE;
entry = &mlu->Entries[idx];
*(cmsUInt16Number *)LanguageCode = _cmsAdjustEndianess16(entry->Language);
*(cmsUInt16Number *)CountryCode = _cmsAdjustEndianess16(entry->Country);
return TRUE;
}
@ -488,8 +517,10 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn
while (v -> Allocated < n)
GrowNamedColorList(v);
strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix));
strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix));
strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);
v->Prefix[32] = v->Suffix[32] = 0;
v -> ColorantCount = ColorantCount;
return v;
@ -498,8 +529,9 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn
// Free a list
void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)
{
if (v == NULL) return;
if (v ->List) _cmsFree(v ->ContextID, v ->List);
if (v) _cmsFree(v ->ContextID, v);
_cmsFree(v ->ContextID, v);
}
cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
@ -543,9 +575,12 @@ cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,
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));
if (Name != NULL) {
strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);
NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;
}
else
NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
@ -618,6 +653,24 @@ void* DupNamedColorList(cmsStage* mpe)
return cmsDupNamedColorList(List);
}
static
void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
{
cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
if (index >= NamedColorList-> nColors) {
cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
}
else {
// Named color always uses Lab
Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0);
Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0);
Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0);
}
}
static
void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
{
@ -636,15 +689,15 @@ void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const c
// Named color lookup element
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList)
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
{
return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
cmsSigNamedColorElemType,
1, 3,
EvalNamedColor,
DupNamedColorList,
FreeNamedColorList,
cmsDupNamedColorList(NamedColorList));
cmsSigNamedColorElemType,
1, UsePCS ? 3 : NamedColorList ->ColorantCount,
UsePCS ? EvalNamedColorPCS : EvalNamedColor,
DupNamedColorList,
FreeNamedColorList,
cmsDupNamedColorList(NamedColorList));
}
@ -680,6 +733,10 @@ cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUI
Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
Seq -> n = n;
if (Seq -> seq == NULL) {
_cmsFree(ContextID, Seq);
return NULL;
}
for (i=0; i < n; i++) {
Seq -> seq[i].Manufacturer = NULL;
@ -745,6 +802,128 @@ Error:
return NULL;
}
// Dictionaries --------------------------------------------------------------------------------------------------------
// Dictionaries are just very simple linked lists
typedef struct _cmsDICT_struct {
cmsDICTentry* head;
cmsContext ContextID;
} _cmsDICT;
// Allocate an empty dictionary
cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID)
{
_cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT));
if (dict == NULL) return NULL;
dict ->ContextID = ContextID;
return (cmsHANDLE) dict;
}
// Dispose resources
void CMSEXPORT cmsDictFree(cmsHANDLE hDict)
{
_cmsDICT* dict = (_cmsDICT*) hDict;
cmsDICTentry *entry, *next;
_cmsAssert(dict != NULL);
// Walk the list freeing all nodes
entry = dict ->head;
while (entry != NULL) {
if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName);
if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue);
if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name);
if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value);
// Don't fall in the habitual trap...
next = entry ->Next;
_cmsFree(dict ->ContextID, entry);
entry = next;
}
_cmsFree(dict ->ContextID, dict);
}
// Duplicate a wide char string
static
wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr)
{
if (ptr == NULL) return NULL;
return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t));
}
// Add a new entry to the linked list
cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue)
{
_cmsDICT* dict = (_cmsDICT*) hDict;
cmsDICTentry *entry;
_cmsAssert(dict != NULL);
_cmsAssert(Name != NULL);
entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry));
if (entry == NULL) return FALSE;
entry ->DisplayName = cmsMLUdup(DisplayName);
entry ->DisplayValue = cmsMLUdup(DisplayValue);
entry ->Name = DupWcs(dict ->ContextID, Name);
entry ->Value = DupWcs(dict ->ContextID, Value);
entry ->Next = dict ->head;
dict ->head = entry;
return TRUE;
}
// Duplicates an existing dictionary
cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict)
{
_cmsDICT* old_dict = (_cmsDICT*) hDict;
cmsHANDLE hNew;
cmsDICTentry *entry;
_cmsAssert(old_dict != NULL);
hNew = cmsDictAlloc(old_dict ->ContextID);
if (hNew == NULL) return NULL;
// Walk the list freeing all nodes
entry = old_dict ->head;
while (entry != NULL) {
if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) {
cmsDictFree(hNew);
return NULL;
}
entry = entry -> Next;
}
return hNew;
}
// Get a pointer to the linked list
const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict)
{
_cmsDICT* dict = (_cmsDICT*) hDict;
if (dict == NULL) return NULL;
return dict ->head;
}
// Helper For external languages
const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e)
{
if (e == NULL) return NULL;
return e ->Next;
}

View File

@ -2,7 +2,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2011 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"),
@ -53,10 +53,6 @@ typedef struct {
int nInputs;
int nOutputs;
// Since there is no limitation of the output number of channels, this buffer holding the connexion CLUT-shaper
// has to be dynamically allocated. This is not the case of first step shaper-CLUT, which is limited to max inputs
cmsUInt16Number* StageDEF;
_cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance
cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS];
@ -174,8 +170,6 @@ cmsBool PreOptimize(cmsPipeline* Lut)
{
cmsBool AnyOpt = FALSE, Opt;
AnyOpt = FALSE;
do {
Opt = FALSE;
@ -195,6 +189,12 @@ cmsBool PreOptimize(cmsPipeline* Lut)
// Remove V2 to V4 followed by V4 to V2
Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2);
// Remove float pcs Lab conversions
Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab);
// Remove float pcs Lab conversions
Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ);
if (Opt) AnyOpt = TRUE;
} while (Opt);
@ -219,6 +219,7 @@ void PrelinEval16(register const cmsUInt16Number Input[],
{
Prelin16Data* p16 = (Prelin16Data*) D;
cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS];
cmsUInt16Number StageDEF[cmsMAXCHANNELS];
int i;
for (i=0; i < p16 ->nInputs; i++) {
@ -226,11 +227,11 @@ void PrelinEval16(register const cmsUInt16Number Input[],
p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]);
}
p16 ->EvalCLUT(StageABC, p16 ->StageDEF, p16 ->CLUTparams);
p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams);
for (i=0; i < p16 ->nOutputs; i++) {
p16 ->EvalCurveOut16[i](&p16->StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]);
p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]);
}
}
@ -240,7 +241,6 @@ void PrelinOpt16free(cmsContext ContextID, void* ptr)
{
Prelin16Data* p16 = (Prelin16Data*) ptr;
_cmsFree(ContextID, p16 ->StageDEF);
_cmsFree(ContextID, p16 ->EvalCurveOut16);
_cmsFree(ContextID, p16 ->ParamsCurveOut16);
@ -255,7 +255,6 @@ void* Prelin16dup(cmsContext ContextID, const void* ptr)
if (Duped == NULL) return NULL;
Duped ->StageDEF = _cmsCalloc(ContextID, p16 ->nOutputs, sizeof(cmsUInt16Number));
Duped ->EvalCurveOut16 = _cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16));
Duped ->ParamsCurveOut16 = _cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* ));
@ -270,7 +269,7 @@ Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
int nOutputs, cmsToneCurve** Out )
{
int i;
Prelin16Data* p16 = (Prelin16Data*) _cmsMallocZero(ContextID, sizeof(Prelin16Data));
Prelin16Data* p16 = _cmsMallocZero(ContextID, sizeof(Prelin16Data));
if (p16 == NULL) return NULL;
p16 ->nInputs = nInputs;
@ -294,7 +293,6 @@ Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16;
p16 -> StageDEF = _cmsCalloc(ContextID, p16 ->nOutputs, sizeof(cmsUInt16Number));
p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16));
p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* ));
@ -379,46 +377,58 @@ cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
int i, index;
if (CLUT -> Type != cmsSigCLutElemType) {
cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut MPE");
cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage");
return FALSE;
}
px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0;
pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0;
pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0;
x0 = (int) floor(px);
y0 = (int) floor(py);
z0 = (int) floor(pz);
w0 = (int) floor(pw);
if (nChannelsIn == 4) {
px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0;
pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0;
pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0;
x0 = (int) floor(px);
y0 = (int) floor(py);
z0 = (int) floor(pz);
w0 = (int) floor(pw);
if (((px - x0) != 0) ||
((py - y0) != 0) ||
((pz - z0) != 0) ||
((pw - w0) != 0)) return FALSE; // Not on exact node
index = p16 -> opta[3] * x0 +
p16 -> opta[2] * y0 +
p16 -> opta[1] * z0 +
p16 -> opta[0] * w0;
p16 -> opta[2] * y0 +
p16 -> opta[1] * z0 +
p16 -> opta[0] * w0;
}
else
if (nChannelsIn == 3) {
px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0;
pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0;
x0 = (int) floor(px);
y0 = (int) floor(py);
z0 = (int) floor(pz);
if (((px - x0) != 0) ||
((py - y0) != 0) ||
((pz - z0) != 0)) return FALSE; // Not on exact node
index = p16 -> opta[2] * x0 +
p16 -> opta[1] * y0 +
p16 -> opta[0] * z0;
p16 -> opta[1] * y0 +
p16 -> opta[0] * z0;
}
else
if (nChannelsIn == 1) {
px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
x0 = (int) floor(px);
if (((px - x0) != 0)) return FALSE; // Not on exact node
index = p16 -> opta[0] * x0;
@ -434,13 +444,15 @@ cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
return TRUE;
}
// Auxiliar, to see if two values are equal.
// Auxiliar, to see if two values are equal or very different
static
cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] )
{
int i;
for (i=0; i < n; i++) {
if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremly different that the fixup should be avoided
if (White1[i] != White2[i]) return FALSE;
}
return TRUE;
@ -463,6 +475,8 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor
&WhitePointOut, NULL, &nOuts)) return FALSE;
// It needs to be fixed?
if (Lut ->InputChannels != nIns) return FALSE;
if (Lut ->OutputChannels != nOuts) return FALSE;
cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut);
@ -498,8 +512,14 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor
for (i=0; i < nOuts; i++) {
cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]);
WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]);
cmsFreeToneCurve(InversePostLin);
if (InversePostLin == NULL) {
WhiteOut[i] = WhitePointOut[i];
} else {
WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]);
cmsFreeToneCurve(InversePostLin);
}
}
}
else {
@ -525,8 +545,9 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor
static
cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
{
cmsPipeline* Src;
cmsPipeline* Dest;
cmsPipeline* Src = NULL;
cmsPipeline* Dest = NULL;
cmsStage* mpe;
cmsStage* CLUT;
cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL;
int nGridPoints;
@ -538,7 +559,6 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
cmsToneCurve** DataSetOut;
Prelin16Data* p16;
// This is a loosy optimization! does not apply in floating-point cases
if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
@ -552,6 +572,13 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
Src = *Lut;
// Named color pipelines cannot be optimized either
for (mpe = cmsPipelineGetPtrToFirstStage(Src);
mpe != NULL;
mpe = cmsStageNext(mpe)) {
if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
}
// Allocate an empty LUT
Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
if (!Dest) return FALSE;
@ -570,7 +597,8 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
// All seems ok, proceed.
NewPreLin = cmsStageDup(PreLin);
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin);
if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin))
goto Error;
// Remove prelinearization. Since we have duplicated the curve
// in destination LUT, the sampling shoud be applied after this stage.
@ -584,7 +612,9 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
if (CLUT == NULL) return FALSE;
// Add the CLUT to the destination LUT
cmsPipelineInsertStage(Dest, cmsAT_END, CLUT);
if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) {
goto Error;
}
// Postlinearization tables are kept unless indicated by flags
if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) {
@ -600,7 +630,8 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
// All seems ok, proceed.
NewPostLin = cmsStageDup(PostLin);
cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin);
if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin))
goto Error;
// In destination LUT, the sampling shoud be applied after this stage.
cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin);
@ -611,10 +642,18 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
// Now its time to do the sampling. We have to ignore pre/post linearization
// The source LUT whithout pre/post curves is passed as parameter.
if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) {
Error:
// Ops, something went wrong, Restore stages
if (KeepPreLin != NULL) cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin);
if (KeepPostLin != NULL) cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin);
if (KeepPreLin != NULL) {
if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) {
_cmsAssert(0); // This never happens
}
}
if (KeepPostLin != NULL) {
if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) {
_cmsAssert(0); // This never happens
}
}
cmsPipelineFree(Dest);
return FALSE;
}
@ -641,12 +680,11 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
else {
p16 = PrelinOpt16alloc(Dest ->ContextID,
DataCLUT ->Params,
Dest ->InputChannels,
DataSetIn,
Dest ->OutputChannels,
DataSetOut);
DataCLUT ->Params,
Dest ->InputChannels,
DataSetIn,
Dest ->OutputChannels,
DataSetOut);
_cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
}
@ -789,8 +827,8 @@ void PrelinEval8(register const cmsUInt16Number Input[],
cmsUInt8Number r, g, b;
cmsS15Fixed16Number rx, ry, rz;
cmsS15Fixed16Number c0, c1, c2, c3, Rest;
int OutChan;
register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
int OutChan;
register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
Prelin8Data* p8 = (Prelin8Data*) D;
register const cmsInterpParams* p = p8 ->p;
int TotalOut = p -> nOutputs;
@ -864,15 +902,35 @@ void PrelinEval8(register const cmsUInt16Number Input[],
}
Rest = c1 * rx + c2 * ry + c3 * rz;
Output[OutChan] = (cmsUInt16Number)c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest));
Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
Output[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest>>16))>>16);
}
}
#undef DENS
// Curves that contain wide empty areas are not optimizeable
static
cmsBool IsDegenerated(const cmsToneCurve* g)
{
int i, Zeros = 0, Poles = 0;
int nEntries = g ->nEntries;
for (i=0; i < nEntries; i++) {
if (g ->Table16[i] == 0x0000) Zeros++;
if (g ->Table16[i] == 0xffff) Poles++;
}
if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables
if (Zeros > (nEntries / 4)) return TRUE; // Degenerated, mostly zeros
if (Poles > (nEntries / 4)) return TRUE; // Degenerated, mostly poles
return FALSE;
}
// --------------------------------------------------------------------------------------------------------------
// We need xput over here
@ -889,6 +947,7 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
cmsStage* OptimizedCLUTmpe;
cmsColorSpaceSignature ColorSpace, OutputColorSpace;
cmsStage* OptimizedPrelinMpe;
cmsStage* mpe;
cmsToneCurve** OptimizedPrelinCurves;
_cmsStageCLutData* OptimizedPrelinCLUT;
@ -907,6 +966,14 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
}
OriginalLut = *Lut;
// Named color pipelines cannot be optimized either
for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut);
mpe != NULL;
mpe = cmsStageNext(mpe)) {
if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
}
ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat));
OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat));
nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
@ -953,6 +1020,9 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
// Exclude if non-monotonic
if (!cmsIsToneCurveMonotonic(Trans[t]))
lIsSuitable = FALSE;
if (IsDegenerated(Trans[t]))
lIsSuitable = FALSE;
}
// If it is not suitable, just quit
@ -968,7 +1038,8 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
LutPlusCurves = cmsPipelineDup(OriginalLut);
if (LutPlusCurves == NULL) goto Error;
cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse));
if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse)))
goto Error;
// Create the result LUT
OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels);
@ -977,13 +1048,15 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans);
// Create and insert the curves at the beginning
cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe);
if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe))
goto Error;
// Allocate the CLUT for result
OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL);
// Add the CLUT to the destination LUT
cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe);
if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe))
goto Error;
// Resample the LUT
if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error;
@ -1112,6 +1185,16 @@ Curves16Data* CurvesAlloc(cmsContext ContextID, int nCurves, int nElements, cmsT
c16->Curves[i] = _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number));
if (c16->Curves[i] == NULL) {
for (j=0; j < i; j++) {
_cmsFree(ContextID, c16->Curves[j]);
}
_cmsFree(ContextID, c16->Curves);
_cmsFree(ContextID, c16);
return NULL;
}
if (nElements == 256) {
for (j=0; j < nElements; j++) {
@ -1237,7 +1320,8 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
// Maybe the curves are linear at the end
if (!AllCurvesAreLinear(ObtainedCurves)) {
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves);
if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves))
goto Error;
// If the curves are to be applied in 8 bits, we can save memory
if (_cmsFormatterIs8bit(*InputFormat)) {
@ -1245,6 +1329,7 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
_cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data;
Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves);
if (c16 == NULL) goto Error;
*dwFlags |= cmsFLAGS_NOCACHE;
_cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup);
@ -1254,6 +1339,7 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
_cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves);
Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves);
if (c16 == NULL) goto Error;
*dwFlags |= cmsFLAGS_NOCACHE;
_cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup);
}
@ -1263,7 +1349,8 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
// LUT optimizes to nothing. Set the identity LUT
cmsStageFree(ObtainedCurves);
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels));
if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels)))
goto Error;
*dwFlags |= cmsFLAGS_NOCACHE;
_cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL);
@ -1385,12 +1472,12 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi
// first we compute the resulting byte and then we store the byte times
// 257. This quantization allows to round very quick by doing a >> 8, but
// since the low byte is always equal to msb, we can do a & 0xff and this works!
cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0 + 0.5);
cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0);
cmsUInt8Number b = FROM_16_TO_8(w);
Table[i] = FROM_8_TO_16(b);
}
else Table[i] = _cmsQuickSaturateWord(Val * 65535.0 + 0.5);
else Table[i] = _cmsQuickSaturateWord(Val * 65535.0);
}
}
@ -1493,10 +1580,14 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
if (!Dest) return FALSE;
// Assamble the new LUT
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1));
if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1)))
goto Error;
if (!IdentityMat)
cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset));
cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2));
if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset)))
goto Error;
if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2)))
goto Error;
// If identity on matrix, we can further optimize the curves, so call the join curves routine
if (IdentityMat) {
@ -1518,6 +1609,10 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
cmsPipelineFree(Src);
*Lut = Dest;
return TRUE;
Error:
// Leave Src unchanged
cmsPipelineFree(Dest);
return FALSE;
}
@ -1544,44 +1639,102 @@ static _cmsOptimizationCollection DefaultOptimization[] = {
};
// The linked list head
static _cmsOptimizationCollection* OptimizationCollection = DefaultOptimization;
_cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL };
// Duplicates the zone of memory used by the plug-in in the new context
static
void DupPluginOptimizationList(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
_cmsOptimizationPluginChunkType newHead = { NULL };
_cmsOptimizationCollection* entry;
_cmsOptimizationCollection* Anterior = NULL;
_cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin];
_cmsAssert(ctx != NULL);
_cmsAssert(head != NULL);
// Walk the list copying all nodes
for (entry = head->OptimizationCollection;
entry != NULL;
entry = entry ->Next) {
_cmsOptimizationCollection *newEntry = ( _cmsOptimizationCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsOptimizationCollection));
if (newEntry == NULL)
return;
// We want to keep the linked list order, so this is a little bit tricky
newEntry -> Next = NULL;
if (Anterior)
Anterior -> Next = newEntry;
Anterior = newEntry;
if (newHead.OptimizationCollection == NULL)
newHead.OptimizationCollection = newEntry;
}
ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsOptimizationPluginChunkType));
}
void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
if (src != NULL) {
// Copy all linked list
DupPluginOptimizationList(ctx, src);
}
else {
static _cmsOptimizationPluginChunkType OptimizationPluginChunkType = { NULL };
ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx ->MemPool, &OptimizationPluginChunkType, sizeof(_cmsOptimizationPluginChunkType));
}
}
// Register new ways to optimize
cmsBool _cmsRegisterOptimizationPlugin(cmsPluginBase* Data)
cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data)
{
cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data;
_cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
_cmsOptimizationCollection* fl;
if (Data == NULL) {
OptimizationCollection = DefaultOptimization;
ctx->OptimizationCollection = NULL;
return TRUE;
}
// Optimizer callback is required
if (Plugin ->OptimizePtr == NULL) return FALSE;
fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(sizeof(_cmsOptimizationCollection));
fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection));
if (fl == NULL) return FALSE;
// Copy the parameters
fl ->OptimizePtr = Plugin ->OptimizePtr;
// Keep linked list
fl ->Next = OptimizationCollection;
OptimizationCollection = fl;
fl ->Next = ctx->OptimizationCollection;
// Set the head
ctx ->OptimizationCollection = fl;
// All is ok
return TRUE;
}
// The entry point for LUT optimization
cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut,
cmsBool _cmsOptimizePipeline(cmsContext ContextID,
cmsPipeline** PtrLut,
int Intent,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags)
{
_cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
_cmsOptimizationCollection* Opts;
cmsBool AnySuccess = FALSE;
@ -1611,8 +1764,8 @@ cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut,
if (*dwFlags & cmsFLAGS_NOOPTIMIZE)
return FALSE;
// Try built-in optimizations and plug-in
for (Opts = OptimizationCollection;
// Try plug-in optimizations
for (Opts = ctx->OptimizationCollection;
Opts != NULL;
Opts = Opts ->Next) {
@ -1623,6 +1776,17 @@ cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut,
}
}
// Try built-in optimizations
for (Opts = DefaultOptimization;
Opts != NULL;
Opts = Opts ->Next) {
if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) {
return TRUE;
}
}
// Only simple optimizations succeeded
return AnySuccess;
}

File diff suppressed because it is too large Load Diff

View File

@ -169,26 +169,26 @@ void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cm
static
cmsFloat64Number L2float2(cmsUInt16Number v)
{
return (cmsFloat64Number) v / 652.800;
return (cmsFloat64Number) v / 652.800;
}
// the a/b part
static
cmsFloat64Number ab2float2(cmsUInt16Number v)
{
return ((cmsFloat64Number) v / 256.0) - 128.0;
return ((cmsFloat64Number) v / 256.0) - 128.0;
}
static
cmsUInt16Number L2Fix2(cmsFloat64Number L)
{
return _cmsQuickSaturateWord(L * 652.8);
return _cmsQuickSaturateWord(L * 652.8);
}
static
cmsUInt16Number ab2Fix2(cmsFloat64Number ab)
{
return _cmsQuickSaturateWord((ab + 128.0) * 256.0);
return _cmsQuickSaturateWord((ab + 128.0) * 256.0);
}
@ -869,8 +869,11 @@ cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
{
switch (ColorSpace) {
case cmsSigMCH1Data:
case cmsSig1colorData:
case cmsSigGrayData: return 1;
case cmsSigMCH2Data:
case cmsSig2colorData: return 2;
case cmsSigXYZData:
@ -882,10 +885,12 @@ cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
case cmsSigHsvData:
case cmsSigHlsData:
case cmsSigCmyData:
case cmsSigMCH3Data:
case cmsSig3colorData: return 3;
case cmsSigLuvKData:
case cmsSigCmykData:
case cmsSigMCH4Data:
case cmsSig4colorData: return 4;
case cmsSigMCH5Data:

View File

@ -76,12 +76,12 @@ cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord)
// 1 2 3 4 5 6 7 8
// 8 7 6 5 4 3 2 1
void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number QWord)
void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord)
{
#ifndef CMS_USE_BIG_ENDIAN
cmsUInt8Number* pIn = (cmsUInt8Number*) &QWord;
cmsUInt8Number* pIn = (cmsUInt8Number*) QWord;
cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
_cmsAssert(Result != NULL);
@ -96,10 +96,14 @@ void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number Q
pOut[0] = pIn[7];
#else
_cmsAssert(Result != NULL);
*Result = QWord;
# ifdef CMS_DONT_USE_INT64
(*Result)[0] = QWord[0];
(*Result)[1] = QWord[1];
# else
*Result = *QWord;
# endif
#endif
}
@ -189,7 +193,7 @@ cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
return FALSE;
if (n != NULL) _cmsAdjustEndianess64(n, tmp);
if (n != NULL) _cmsAdjustEndianess64(n, &tmp);
return TRUE;
}
@ -311,7 +315,7 @@ cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number n)
cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
{
cmsUInt64Number tmp;
@ -511,20 +515,34 @@ cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
// Plugin memory management -------------------------------------------------------------------------------------------------
static _cmsSubAllocator* PluginPool = NULL;
// Specialized malloc for plug-ins, that is freed upon exit.
void* _cmsPluginMalloc(cmsUInt32Number size)
void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
{
if (PluginPool == NULL)
PluginPool = _cmsCreateSubAlloc(0, 4*1024);
struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
return _cmsSubAlloc(PluginPool, size);
if (ctx ->MemPool == NULL) {
if (ContextID == NULL) {
ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
}
else {
cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
return NULL;
}
}
return _cmsSubAlloc(ctx->MemPool, size);
}
// Main plug-in dispatcher
cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
{
return cmsPluginTHR(NULL, Plug_in);
}
cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
{
cmsPluginBase* Plugin;
@ -533,56 +551,64 @@ cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
Plugin = Plugin -> Next) {
if (Plugin -> Magic != cmsPluginMagicNumber) {
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
cmsSignalError(id, 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;
}
if (Plugin ->ExpectedVersion > LCMS_VERSION) {
cmsSignalError(id, 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;
if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginInterpolationSig:
if (!_cmsRegisterInterpPlugin(Plugin)) return FALSE;
if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginTagTypeSig:
if (!_cmsRegisterTagTypePlugin(Plugin)) return FALSE;
if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
break;
case cmsPluginTagSig:
if (!_cmsRegisterTagPlugin(Plugin)) return FALSE;
if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginFormattersSig:
if (!_cmsRegisterFormattersPlugin(Plugin)) return FALSE;
if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginRenderingIntentSig:
if (!_cmsRegisterRenderingIntentPlugin(Plugin)) return FALSE;
if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginParametricCurveSig:
if (!_cmsRegisterParametricCurvesPlugin(Plugin)) return FALSE;
if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginMultiProcessElementSig:
if (!_cmsRegisterMultiProcessElementPlugin(Plugin)) return FALSE;
if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginOptimizationSig:
if (!_cmsRegisterOptimizationPlugin(Plugin)) return FALSE;
if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginTransformSig:
if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
break;
case cmsPluginMutexSig:
if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
break;
default:
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
return FALSE;
}
}
@ -595,18 +621,337 @@ cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
// 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;
cmsUnregisterPluginsTHR(NULL);
}
// The Global storage for system context. This is the one and only global variable
// pointers structure. All global vars are referenced here.
static struct _cmsContext_struct globalContext = {
NULL, // Not in the linked list
NULL, // No suballocator
{
NULL, // UserPtr,
&_cmsLogErrorChunk, // Logger,
&_cmsAlarmCodesChunk, // AlarmCodes,
&_cmsAdaptationStateChunk, // AdaptationState,
&_cmsMemPluginChunk, // MemPlugin,
&_cmsInterpPluginChunk, // InterpPlugin,
&_cmsCurvesPluginChunk, // CurvesPlugin,
&_cmsFormattersPluginChunk, // FormattersPlugin,
&_cmsTagTypePluginChunk, // TagTypePlugin,
&_cmsTagPluginChunk, // TagPlugin,
&_cmsIntentsPluginChunk, // IntentPlugin,
&_cmsMPETypePluginChunk, // MPEPlugin,
&_cmsOptimizationPluginChunk, // OptimizationPlugin,
&_cmsTransformPluginChunk, // TransformPlugin,
&_cmsMutexPluginChunk // MutexPlugin
},
{ NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
};
// The context pool (linked list head)
static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
// Internal, get associated pointer, with guessing. Never returns NULL.
struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
{
struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
struct _cmsContext_struct* ctx;
// On 0, use global settings
if (id == NULL)
return &globalContext;
// Search
for (ctx = _cmsContextPoolHead;
ctx != NULL;
ctx = ctx ->Next) {
// Found it?
if (id == ctx)
return ctx; // New-style context,
}
return &globalContext;
}
// Internal: get the memory area associanted with each context client
// Returns the block assigned to the specific zone.
void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
{
struct _cmsContext_struct* ctx;
void *ptr;
if (mc < 0 || mc >= MemoryClientMax) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Bad context client");
return NULL;
}
ctx = _cmsGetContext(ContextID);
ptr = ctx ->chunks[mc];
if (ptr != NULL)
return ptr;
// A null ptr means no special settings for that context, and this
// reverts to Context0 globals
return globalContext.chunks[mc];
}
// This function returns the given context its default pristine state,
// as no plug-ins were declared. There is no way to unregister a single
// plug-in, as a single call to cmsPluginTHR() function may register
// many different plug-ins simultaneously, then there is no way to
// identify which plug-in to unregister.
void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
{
_cmsRegisterMemHandlerPlugin(ContextID, NULL);
_cmsRegisterInterpPlugin(ContextID, NULL);
_cmsRegisterTagTypePlugin(ContextID, NULL);
_cmsRegisterTagPlugin(ContextID, NULL);
_cmsRegisterFormattersPlugin(ContextID, NULL);
_cmsRegisterRenderingIntentPlugin(ContextID, NULL);
_cmsRegisterParametricCurvesPlugin(ContextID, NULL);
_cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
_cmsRegisterOptimizationPlugin(ContextID, NULL);
_cmsRegisterTransformPlugin(ContextID, NULL);
_cmsRegisterMutexPlugin(ContextID, NULL);
}
// Returns the memory manager plug-in, if any, from the Plug-in bundle
static
cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
{
cmsPluginBase* Plugin;
for (Plugin = (cmsPluginBase*) PluginBundle;
Plugin != NULL;
Plugin = Plugin -> Next) {
if (Plugin -> Magic == cmsPluginMagicNumber &&
Plugin -> ExpectedVersion <= LCMS_VERSION &&
Plugin -> Type == cmsPluginMemHandlerSig) {
// Found!
return (cmsPluginMemHandler*) Plugin;
}
}
// Nope, revert to defaults
return NULL;
}
// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined
// data that will be forwarded to plug-ins and logger.
cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
{
struct _cmsContext_struct* ctx;
struct _cmsContext_struct fakeContext;
_cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
fakeContext.chunks[UserPtr] = UserData;
fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager;
// Create the context structure.
ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
if (ctx == NULL)
return NULL; // Something very wrong happened!
// Init the structure and the memory manager
memset(ctx, 0, sizeof(struct _cmsContext_struct));
// Keep memory manager
memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk));
// Maintain the linked list (with proper locking)
_cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
ctx ->Next = _cmsContextPoolHead;
_cmsContextPoolHead = ctx;
_cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
ctx ->chunks[UserPtr] = UserData;
ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager;
// Now we can allocate the pool by using default memory manager
ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); // default size about 32 pointers
if (ctx ->MemPool == NULL) {
cmsDeleteContext(ctx);
return NULL;
}
_cmsAllocLogErrorChunk(ctx, NULL);
_cmsAllocAlarmCodesChunk(ctx, NULL);
_cmsAllocAdaptationStateChunk(ctx, NULL);
_cmsAllocMemPluginChunk(ctx, NULL);
_cmsAllocInterpPluginChunk(ctx, NULL);
_cmsAllocCurvesPluginChunk(ctx, NULL);
_cmsAllocFormattersPluginChunk(ctx, NULL);
_cmsAllocTagTypePluginChunk(ctx, NULL);
_cmsAllocMPETypePluginChunk(ctx, NULL);
_cmsAllocTagPluginChunk(ctx, NULL);
_cmsAllocIntentsPluginChunk(ctx, NULL);
_cmsAllocOptimizationPluginChunk(ctx, NULL);
_cmsAllocTransformPluginChunk(ctx, NULL);
_cmsAllocMutexPluginChunk(ctx, NULL);
// Setup the plug-ins
if (!cmsPluginTHR(ctx, Plugin)) {
cmsDeleteContext(ctx);
return NULL;
}
return (cmsContext) ctx;
}
// Duplicates a context with all associated plug-ins.
// Caller may specify an optional pointer to user-defined
// data that will be forwarded to plug-ins and logger.
cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
{
int i;
struct _cmsContext_struct* ctx;
const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
if (ctx == NULL)
return NULL; // Something very wrong happened
// Setup default memory allocators
memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
// Maintain the linked list
_cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
ctx ->Next = _cmsContextPoolHead;
_cmsContextPoolHead = ctx;
_cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
ctx ->chunks[UserPtr] = userData;
ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager;
ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
if (ctx ->MemPool == NULL) {
cmsDeleteContext(ctx);
return NULL;
}
// Allocate all required chunks.
_cmsAllocLogErrorChunk(ctx, src);
_cmsAllocAlarmCodesChunk(ctx, src);
_cmsAllocAdaptationStateChunk(ctx, src);
_cmsAllocMemPluginChunk(ctx, src);
_cmsAllocInterpPluginChunk(ctx, src);
_cmsAllocCurvesPluginChunk(ctx, src);
_cmsAllocFormattersPluginChunk(ctx, src);
_cmsAllocTagTypePluginChunk(ctx, src);
_cmsAllocMPETypePluginChunk(ctx, src);
_cmsAllocTagPluginChunk(ctx, src);
_cmsAllocIntentsPluginChunk(ctx, src);
_cmsAllocOptimizationPluginChunk(ctx, src);
_cmsAllocTransformPluginChunk(ctx, src);
_cmsAllocMutexPluginChunk(ctx, src);
// Make sure no one failed
for (i=Logger; i < MemoryClientMax; i++) {
if (src ->chunks[i] == NULL) {
cmsDeleteContext((cmsContext) ctx);
return NULL;
}
}
return (cmsContext) ctx;
}
static
struct _cmsContext_struct* FindPrev(struct _cmsContext_struct* id)
{
struct _cmsContext_struct* prev;
// Search for previous
for (prev = _cmsContextPoolHead;
prev != NULL;
prev = prev ->Next)
{
if (prev ->Next == id)
return prev;
}
return NULL; // List is empty or only one element!
}
// Frees any resources associated with the given context,
// and destroys the context placeholder.
// The ContextID can no longer be used in any THR operation.
void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
{
if (ContextID != NULL) {
struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
struct _cmsContext_struct fakeContext;
struct _cmsContext_struct* prev;
memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr];
fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager;
// Get rid of plugins
cmsUnregisterPluginsTHR(ContextID);
// Since all memory is allocated in the private pool, all what we need to do is destroy the pool
if (ctx -> MemPool != NULL)
_cmsSubAllocDestroy(ctx ->MemPool);
ctx -> MemPool = NULL;
// Maintain list
_cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
if (_cmsContextPoolHead == ctx) {
_cmsContextPoolHead = ctx->Next;
}
else {
// Search for previous
for (prev = _cmsContextPoolHead;
prev != NULL;
prev = prev ->Next)
{
if (prev -> Next == ctx) {
prev -> Next = ctx ->Next;
break;
}
}
}
_cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
// free the memory block itself
_cmsFree(&fakeContext, ctx);
}
}
// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
{
return _cmsContextGetClientChunk(ContextID, UserPtr);
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2008 Marti Maria Saguer
// Copyright (c) 1998-2011 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"),
@ -266,20 +266,20 @@
// This struct holds the memory block currently being write
typedef struct {
_cmsStageCLutData* Pipeline;
cmsIOHANDLER* m;
_cmsStageCLutData* Pipeline;
cmsIOHANDLER* m;
int FirstComponent;
int SecondComponent;
int FirstComponent;
int SecondComponent;
const char* PreMaj;
const char* PostMaj;
const char* PreMin;
const char* PostMin;
const char* PreMaj;
const char* PostMaj;
const char* PreMin;
const char* PostMin;
int FixWhite; // Force mapping of pure white
int FixWhite; // Force mapping of pure white
cmsColorSpaceSignature ColorSpace; // ColorSpace of profile
cmsColorSpaceSignature ColorSpace; // ColorSpace of profile
} cmsPsSamplerCargo;
@ -300,9 +300,9 @@ cmsUInt8Number Word2Byte(cmsUInt16Number w)
static
cmsUInt8Number L2Byte(cmsUInt16Number w)
{
int ww = w + 0x0080;
int ww = w + 0x0080;
if (ww > 0xFFFF) return 0xFF;
if (ww > 0xFFFF) return 0xFF;
return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF);
}
@ -313,14 +313,14 @@ cmsUInt8Number L2Byte(cmsUInt16Number w)
static
void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
{
_cmsIOPrintf(m, "%02x", b);
_cmsPSActualColumn += 2;
_cmsIOPrintf(m, "%02x", b);
_cmsPSActualColumn += 2;
if (_cmsPSActualColumn > MAXPSCOLS) {
if (_cmsPSActualColumn > MAXPSCOLS) {
_cmsIOPrintf(m, "\n");
_cmsPSActualColumn = 0;
}
_cmsIOPrintf(m, "\n");
_cmsPSActualColumn = 0;
}
}
// ----------------------------------------------------------------- PostScript generation
@ -346,19 +346,19 @@ static
void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)
{
time_t timer;
cmsMLU *Description, *Copyright;
char DescASCII[256], CopyrightASCII[256];
cmsMLU *Description, *Copyright;
char DescASCII[256], CopyrightASCII[256];
time(&timer);
Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
DescASCII[0] = DescASCII[255] = 0;
DescASCII[0] = DescASCII[255] = 0;
CopyrightASCII[0] = CopyrightASCII[255] = 0;
if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255);
if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255);
if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255);
if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255);
_cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");
_cmsIOPrintf(m, "%%\n");
@ -451,7 +451,7 @@ void EmitLab2XYZ(cmsIOHANDLER* m)
_cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
_cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
_cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
_cmsIOPrintf(m, "/DecodeLMN [\n");
_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");
_cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
@ -469,6 +469,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
cmsUInt32Number i;
cmsFloat64Number gamma;
if (Table == NULL) return; // Error
if (Table ->nEntries <= 0) return; // Empty table
@ -476,7 +477,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
if (cmsIsToneCurveLinear(Table)) return;
// Check if is really an exponential. If so, emit "exp"
gamma = cmsEstimateGamma(Table, 0.001);
gamma = cmsEstimateGamma(Table, 0.001);
if (gamma > 0) {
_cmsIOPrintf(m, "{ %g exp } bind ", gamma);
return;
@ -495,7 +496,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
_cmsIOPrintf(m, " [");
for (i=0; i < Table->nEntries; i++) {
_cmsIOPrintf(m, "%d ", Table->Table16[i]);
_cmsIOPrintf(m, "%d ", Table->Table16[i]);
}
_cmsIOPrintf(m, "] "); // v tab
@ -548,12 +549,14 @@ void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])
for( i=0; i < n; i++ )
{
if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
if (g[i] == NULL) return; // Error
if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) {
_cmsIOPrintf(m, "dup ");
}
else {
Emit1Gamma(m, g[i]);
Emit1Gamma(m, g[i]);
}
}
@ -638,21 +641,21 @@ int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Nu
sc ->SecondComponent = In[1];
}
// Dump table.
// Dump table.
for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
cmsUInt16Number wWordOut = Out[i];
cmsUInt16Number wWordOut = Out[i];
cmsUInt8Number wByteOut; // Value as byte
// We always deal with Lab4
// We always deal with Lab4
wByteOut = Word2Byte(wWordOut);
WriteByte(sc -> m, wByteOut);
}
wByteOut = Word2Byte(wWordOut);
WriteByte(sc -> m, wByteOut);
}
return 1;
return 1;
}
// Writes a Pipeline on memstream. Could be 8 or 16 bits based
@ -670,7 +673,7 @@ void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
sc.FirstComponent = -1;
sc.SecondComponent = -1;
sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
sc.m = m;
sc.PreMaj = PreMaj;
sc.PostMaj= PostMaj;
@ -682,8 +685,8 @@ void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
_cmsIOPrintf(m, "[");
for (i=0; i < sc.Pipeline->Params->nInputs; i++)
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
for (i=0; i < sc.Pipeline->Params->nInputs; i++)
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
_cmsIOPrintf(m, " [\n");
@ -702,25 +705,25 @@ static
int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
{
_cmsIOPrintf(m, "[ /CIEBasedA\n");
_cmsIOPrintf(m, " <<\n");
_cmsIOPrintf(m, "[ /CIEBasedA\n");
_cmsIOPrintf(m, " <<\n");
_cmsIOPrintf(m, "/DecodeA ");
_cmsIOPrintf(m, "/DecodeA ");
Emit1Gamma(m, Curve);
Emit1Gamma(m, Curve);
_cmsIOPrintf(m, " \n");
_cmsIOPrintf(m, " \n");
_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
EmitWhiteBlackD50(m, BlackPoint);
EmitIntent(m, INTENT_PERCEPTUAL);
EmitWhiteBlackD50(m, BlackPoint);
EmitIntent(m, INTENT_PERCEPTUAL);
_cmsIOPrintf(m, ">>\n");
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, ">>\n");
_cmsIOPrintf(m, "]\n");
return 1;
return 1;
}
@ -729,38 +732,38 @@ int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
static
int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
{
int i;
int i;
_cmsIOPrintf(m, "[ /CIEBasedABC\n");
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "/DecodeABC [ ");
_cmsIOPrintf(m, "[ /CIEBasedABC\n");
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "/DecodeABC [ ");
EmitNGamma(m, 3, CurveSet);
EmitNGamma(m, 3, CurveSet);
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "/MatrixABC [ " );
_cmsIOPrintf(m, "/MatrixABC [ " );
for( i=0; i < 3; i++ ) {
for( i=0; i < 3; i++ ) {
_cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
Matrix[i + 3*1],
Matrix[i + 3*2]);
}
}
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
EmitWhiteBlackD50(m, BlackPoint);
EmitIntent(m, INTENT_PERCEPTUAL);
EmitWhiteBlackD50(m, BlackPoint);
EmitIntent(m, INTENT_PERCEPTUAL);
_cmsIOPrintf(m, ">>\n");
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, ">>\n");
_cmsIOPrintf(m, "]\n");
return 1;
return 1;
}
@ -770,12 +773,11 @@ int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXY
const char* PreMaj;
const char* PostMaj;
const char* PreMin, *PostMin;
cmsStage* mpe;
cmsStage* mpe;
mpe = Pipeline ->Elements;
mpe = Pipeline ->Elements;
switch (cmsStageInputChannels(mpe)) {
switch (cmsStageInputChannels(mpe)) {
case 3:
_cmsIOPrintf(m, "[ /CIEBasedDEF\n");
@ -797,18 +799,16 @@ int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXY
_cmsIOPrintf(m, "<<\n");
if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
_cmsIOPrintf(m, "/DecodeDEF [ ");
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
_cmsIOPrintf(m, "]\n");
mpe = mpe ->Next;
mpe = mpe ->Next;
}
if (cmsStageType(mpe) == cmsSigCLutElemType) {
if (cmsStageType(mpe) == cmsSigCLutElemType) {
_cmsIOPrintf(m, "/Table ");
WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
@ -822,28 +822,29 @@ int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXY
_cmsIOPrintf(m, " >>\n");
_cmsIOPrintf(m, "]\n");
return 1;
}
// Generates a curve from a gray profile
static
cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
{
cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
cmsHPROFILE hXYZ = cmsCreateXYZProfile();
cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
int i;
for (i=0; i < 256; i++) {
if (Out != NULL) {
for (i=0; i < 256; i++) {
cmsUInt8Number Gray = (cmsUInt8Number) i;
cmsCIEXYZ XYZ;
cmsUInt8Number Gray = (cmsUInt8Number) i;
cmsCIEXYZ XYZ;
cmsDoTransform(xform, &Gray, &XYZ, 1);
cmsDoTransform(xform, &Gray, &XYZ, 1);
Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
}
}
cmsDeleteTransform(xform);
@ -874,29 +875,29 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Nu
nChannels = T_CHANNELS(InputFormat);
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
// Adjust output to Lab4
// Adjust output to Lab4
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
Profiles[0] = hProfile;
Profiles[1] = hLab;
Profiles[0] = hProfile;
Profiles[1] = hLab;
xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
cmsCloseProfile(hLab);
xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
cmsCloseProfile(hLab);
if (xform == NULL) {
if (xform == NULL) {
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
return 0;
}
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
return 0;
}
// Only 1, 3 and 4 channels are allowed
switch (nChannels) {
case 1: {
cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
cmsFreeToneCurve(Gray2Y);
}
@ -904,24 +905,25 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Nu
case 3:
case 4: {
cmsUInt32Number OutFrm = TYPE_Lab_16;
cmsUInt32Number OutFrm = TYPE_Lab_16;
cmsPipeline* DeviceLink;
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
DeviceLink = cmsPipelineDup(v ->Lut);
if (DeviceLink == NULL) return 0;
DeviceLink = cmsPipelineDup(v ->Lut);
if (DeviceLink == NULL) return 0;
dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
cmsPipelineFree(DeviceLink);
if (rc == 0) return 0;
}
break;
default:
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
return 0;
}
@ -955,8 +957,8 @@ int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matr
if (ColorSpace == cmsSigGrayData) {
cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
}
else
@ -972,16 +974,16 @@ int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matr
Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat,
_cmsStageGetPtrToCurveSet(Shaper),
&BlackPointAdaptedToD50);
_cmsStageGetPtrToCurveSet(Shaper),
&BlackPointAdaptedToD50);
}
else {
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
return 0;
}
return rc;
return rc;
}
@ -998,11 +1000,11 @@ int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
char ColorName[32];
cmsNAMEDCOLORLIST* NamedColorList;
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
if (xform == NULL) return 0;
NamedColorList = cmsGetNamedColorList(xform);
NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0;
_cmsIOPrintf(m, "<<\n");
@ -1040,66 +1042,66 @@ int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
// Does create a Color Space Array on XYZ colorspace for PostScript usage
static
cmsUInt32Number GenerateCSA(cmsContext ContextID,
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number dwFlags,
cmsIOHANDLER* mem)
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number dwFlags,
cmsIOHANDLER* mem)
{
cmsUInt32Number dwBytesUsed;
cmsPipeline* lut = NULL;
cmsStage* Matrix, *Shaper;
cmsUInt32Number dwBytesUsed;
cmsPipeline* lut = NULL;
cmsStage* Matrix, *Shaper;
// Is a named color profile?
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
// Is a named color profile?
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
}
else {
if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
}
else {
// Any profile class are allowed (including devicelink), but
// output (PCS) colorspace must be XYZ or Lab
cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
// Any profile class are allowed (including devicelink), but
// output (PCS) colorspace must be XYZ or Lab
cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
if (ColorSpace != cmsSigXYZData &&
ColorSpace != cmsSigLabData) {
if (ColorSpace != cmsSigXYZData &&
ColorSpace != cmsSigLabData) {
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
goto Error;
}
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
goto Error;
}
// Read the lut with all necessary conversion stages
lut = _cmsReadInputLUT(hProfile, Intent);
if (lut == NULL) goto Error;
// Read the lut with all necessary conversion stages
lut = _cmsReadInputLUT(hProfile, Intent);
if (lut == NULL) goto Error;
// Tone curves + matrix can be implemented without any LUT
if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
// Tone curves + matrix can be implemented without any LUT
if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
}
else {
// We need a LUT for the rest
if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
}
}
}
else {
// We need a LUT for the rest
if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
}
}
// Done, keep memory usage
dwBytesUsed = mem ->UsedSpace;
// Done, keep memory usage
dwBytesUsed = mem ->UsedSpace;
// Get rid of LUT
if (lut != NULL) cmsPipelineFree(lut);
// Get rid of LUT
if (lut != NULL) cmsPipelineFree(lut);
// Finally, return used byte count
return dwBytesUsed;
// Finally, return used byte count
return dwBytesUsed;
Error:
if (lut != NULL) cmsPipelineFree(lut);
return 0;
if (lut != NULL) cmsPipelineFree(lut);
return 0;
}
// ------------------------------------------------------ Color Rendering Dictionary (CRD)
@ -1176,15 +1178,15 @@ void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsol
if (lIsAbsolute) {
// For absolute colorimetric intent, encode back to relative
// and generate a relative Pipeline
// and generate a relative Pipeline
// Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
// Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
cmsCIEXYZ White;
cmsCIEXYZ White;
_cmsReadMediaWhitePoint(&White, hProfile);
_cmsReadMediaWhitePoint(&White, hProfile);
_cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
_cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
_cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
_cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
@ -1192,7 +1194,7 @@ void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsol
"{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
"{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
"{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
White.X, White.Y, White.Z);
White.X, White.Y, White.Z);
return;
}
@ -1285,50 +1287,50 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32N
cmsCIEXYZ BlackPointAdaptedToD50;
cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
cmsUInt32Number InFrm = TYPE_Lab_16;
int RelativeEncodingIntent;
cmsColorSpaceSignature ColorSpace;
cmsUInt32Number InFrm = TYPE_Lab_16;
int RelativeEncodingIntent;
cmsColorSpaceSignature ColorSpace;
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
if (hLab == NULL) return 0;
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
if (hLab == NULL) return 0;
OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
nChannels = T_CHANNELS(OutputFormat);
nChannels = T_CHANNELS(OutputFormat);
ColorSpace = cmsGetColorSpace(hProfile);
ColorSpace = cmsGetColorSpace(hProfile);
// For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
// For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
RelativeEncodingIntent = Intent;
if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
// Use V4 Lab always
Profiles[0] = hLab;
Profiles[1] = hProfile;
// Use V4 Lab always
Profiles[0] = hLab;
Profiles[1] = hProfile;
xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
Profiles, 2, TYPE_Lab_DBL,
OutputFormat, RelativeEncodingIntent, 0);
cmsCloseProfile(hLab);
xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
Profiles, 2, TYPE_Lab_DBL,
OutputFormat, RelativeEncodingIntent, 0);
cmsCloseProfile(hLab);
if (xform == NULL) {
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
return 0;
}
// Get a copy of the internal devicelink
v = (_cmsTRANSFORM*) xform;
DeviceLink = cmsPipelineDup(v ->Lut);
if (DeviceLink == NULL) return 0;
if (DeviceLink == NULL) return 0;
// We need a CLUT
dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
// We need a CLUT
dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "/ColorRenderingType 1\n");
@ -1413,19 +1415,19 @@ int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cms
cmsUInt32Number OutputFormat;
char ColorName[32];
char Colorant[128];
cmsNAMEDCOLORLIST* NamedColorList;
cmsNAMEDCOLORLIST* NamedColorList;
OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
nColorant = T_CHANNELS(OutputFormat);
nColorant = T_CHANNELS(OutputFormat);
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
if (xform == NULL) return 0;
NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0;
NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0;
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
@ -1468,128 +1470,128 @@ int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cms
static
cmsUInt32Number GenerateCRD(cmsContext ContextID,
cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
cmsIOHANDLER* mem)
cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
cmsIOHANDLER* mem)
{
cmsUInt32Number dwBytesUsed;
cmsUInt32Number dwBytesUsed;
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
}
EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
}
// Is a named color profile?
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
// Is a named color profile?
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
return 0;
}
}
else {
if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
return 0;
}
}
else {
// CRD are always implemented as LUT
// CRD are always implemented as LUT
if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
return 0;
}
}
if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
return 0;
}
}
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
_cmsIOPrintf(mem, "%%%%EndResource\n");
_cmsIOPrintf(mem, "\n%% CRD End\n");
}
_cmsIOPrintf(mem, "%%%%EndResource\n");
_cmsIOPrintf(mem, "\n%% CRD End\n");
}
// Done, keep memory usage
dwBytesUsed = mem ->UsedSpace;
// Done, keep memory usage
dwBytesUsed = mem ->UsedSpace;
// Finally, return used byte count
return dwBytesUsed;
// Finally, return used byte count
return dwBytesUsed;
cmsUNUSED_PARAMETER(ContextID);
cmsUNUSED_PARAMETER(ContextID);
}
cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
cmsPSResourceType Type,
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number dwFlags,
cmsIOHANDLER* io)
cmsPSResourceType Type,
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number dwFlags,
cmsIOHANDLER* io)
{
cmsUInt32Number rc;
cmsUInt32Number rc;
switch (Type) {
switch (Type) {
case cmsPS_RESOURCE_CSA:
rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
break;
case cmsPS_RESOURCE_CSA:
rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
break;
default:
case cmsPS_RESOURCE_CRD:
rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
break;
}
default:
case cmsPS_RESOURCE_CRD:
rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
break;
}
return rc;
return rc;
}
cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
cmsHPROFILE hProfile,
cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number dwFlags,
void* Buffer, cmsUInt32Number dwBufferLen)
{
cmsIOHANDLER* mem;
cmsIOHANDLER* mem;
cmsUInt32Number dwBytesUsed;
// Set up the serialization engine
if (Buffer == NULL)
mem = cmsOpenIOhandlerFromNULL(ContextID);
else
if (Buffer == NULL)
mem = cmsOpenIOhandlerFromNULL(ContextID);
else
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
if (!mem) return 0;
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
// Get rid of memory stream
cmsCloseIOhandler(mem);
cmsCloseIOhandler(mem);
return dwBytesUsed;
return dwBytesUsed;
}
// Does create a Color Space Array on XYZ colorspace for PostScript usage
cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number dwFlags,
void* Buffer,
cmsUInt32Number dwBufferLen)
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number dwFlags,
void* Buffer,
cmsUInt32Number dwBufferLen)
{
cmsIOHANDLER* mem;
cmsIOHANDLER* mem;
cmsUInt32Number dwBytesUsed;
if (Buffer == NULL)
mem = cmsOpenIOhandlerFromNULL(ContextID);
else
if (Buffer == NULL)
mem = cmsOpenIOhandlerFromNULL(ContextID);
else
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
if (!mem) return 0;
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
// Get rid of memory stream
cmsCloseIOhandler(mem);
cmsCloseIOhandler(mem);
return dwBytesUsed;
return dwBytesUsed;
}

View File

@ -27,6 +27,8 @@
#include "lcms2_internal.h"
#define cmsmin(a, b) (((a) < (b)) ? (a) : (b))
#define cmsmax(a, b) (((a) > (b)) ? (a) : (b))
// This file contains routines for resampling and LUT optimization, black point detection
// and black preservation.
@ -38,13 +40,13 @@
static
cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
{
cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
cmsContext ContextID = cmsGetProfileContextID(hProfile);
cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, 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);
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;
@ -112,8 +114,8 @@ cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
cmsCloseProfile(hLab);
if (xform == NULL) {
// Something went wrong. Get rid of open resources and return zero as black
// Something went wrong. Get rid of open resources and return zero as black
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
@ -144,7 +146,6 @@ cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
static
cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile)
{
cmsHTRANSFORM hRoundTrip;
cmsCIELab LabIn, LabOut;
@ -187,20 +188,29 @@ cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfi
// 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.
cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
{
cmsProfileClassSignature devClass;
// Zero for black point
if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) {
// Make sure the device class is adequate
devClass = cmsGetDeviceClass(hProfile);
if (devClass == cmsSigLinkClass ||
devClass == cmsSigAbstractClass ||
devClass == cmsSigNamedColorClass) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
// Make sure intent is adequate
if (Intent != INTENT_PERCEPTUAL &&
Intent != INTENT_RELATIVE_COLORIMETRIC &&
Intent != INTENT_SATURATION) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// 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)) {
@ -264,3 +274,299 @@ cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfil
}
// ---------------------------------------------------------------------------------------------------------
// Least Squares Fit of a Quadratic Curve to Data
// http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html
static
cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[])
{
double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0;
double sum_y = 0, sum_yx = 0, sum_yx2 = 0;
double d, a, b, c;
int i;
cmsMAT3 m;
cmsVEC3 v, res;
if (n < 4) return 0;
for (i=0; i < n; i++) {
double xn = x[i];
double yn = y[i];
sum_x += xn;
sum_x2 += xn*xn;
sum_x3 += xn*xn*xn;
sum_x4 += xn*xn*xn*xn;
sum_y += yn;
sum_yx += yn*xn;
sum_yx2 += yn*xn*xn;
}
_cmsVEC3init(&m.v[0], n, sum_x, sum_x2);
_cmsVEC3init(&m.v[1], sum_x, sum_x2, sum_x3);
_cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4);
_cmsVEC3init(&v, sum_y, sum_yx, sum_yx2);
if (!_cmsMAT3solve(&res, &m, &v)) return 0;
a = res.n[2];
b = res.n[1];
c = res.n[0];
if (fabs(a) < 1.0E-10) {
return cmsmin(0, cmsmax(50, -c/b ));
}
else {
d = b*b - 4.0 * a * c;
if (d <= 0) {
return 0;
}
else {
double rt = (-b + sqrt(d)) / (2.0 * a);
return cmsmax(0, cmsmin(50, rt));
}
}
}
/*
static
cmsBool IsMonotonic(int n, const cmsFloat64Number Table[])
{
int i;
cmsFloat64Number last;
last = Table[n-1];
for (i = n-2; i >= 0; --i) {
if (Table[i] > last)
return FALSE;
else
last = Table[i];
}
return TRUE;
}
*/
// Calculates the black point of a destination profile.
// This algorithm comes from the Adobe paper disclosing its black point compensation method.
cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
{
cmsColorSpaceSignature ColorSpace;
cmsHTRANSFORM hRoundTrip = NULL;
cmsCIELab InitialLab, destLab, Lab;
cmsFloat64Number inRamp[256], outRamp[256];
cmsFloat64Number MinL, MaxL;
cmsBool NearlyStraightMidrange = TRUE;
cmsFloat64Number yRamp[256];
cmsFloat64Number x[256], y[256];
cmsFloat64Number lo, hi;
int n, l;
cmsProfileClassSignature devClass;
// Make sure the device class is adequate
devClass = cmsGetDeviceClass(hProfile);
if (devClass == cmsSigLinkClass ||
devClass == cmsSigAbstractClass ||
devClass == cmsSigNamedColorClass) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// Make sure intent is adequate
if (Intent != INTENT_PERCEPTUAL &&
Intent != INTENT_RELATIVE_COLORIMETRIC &&
Intent != INTENT_SATURATION) {
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// 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)) {
// Matrix shaper share MRC & perceptual intents
if (cmsIsMatrixShaper(hProfile))
return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);
// 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;
return TRUE;
}
// Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document)
ColorSpace = cmsGetColorSpace(hProfile);
if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) ||
(ColorSpace != cmsSigGrayData &&
ColorSpace != cmsSigRgbData &&
ColorSpace != cmsSigCmykData)) {
// In this case, handle as input case
return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags);
}
// It is one of the valid cases!, use Adobe algorithm
// Set a first guess, that should work on good profiles.
if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
cmsCIEXYZ IniXYZ;
// calculate initial Lab as source black point
if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) {
return FALSE;
}
// convert the XYZ to lab
cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ);
} else {
// set the initial Lab to zero, that should be the black point for perceptual and saturation
InitialLab.L = 0;
InitialLab.a = 0;
InitialLab.b = 0;
}
// Step 2
// ======
// Create a roundtrip. Define a Transform BT for all x in L*a*b*
hRoundTrip = CreateRoundtripXForm(hProfile, Intent);
if (hRoundTrip == NULL) return FALSE;
// Compute ramps
for (l=0; l < 256; l++) {
Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0;
Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a));
Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b));
cmsDoTransform(hRoundTrip, &Lab, &destLab, 1);
inRamp[l] = Lab.L;
outRamp[l] = destLab.L;
}
// Make monotonic
for (l = 254; l > 0; --l) {
outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]);
}
// Check
if (! (outRamp[0] < outRamp[255])) {
cmsDeleteTransform(hRoundTrip);
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// Test for mid range straight (only on relative colorimetric)
NearlyStraightMidrange = TRUE;
MinL = outRamp[0]; MaxL = outRamp[255];
if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
for (l=0; l < 256; l++) {
if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) ||
(fabs(inRamp[l] - outRamp[l]) < 4.0 )))
NearlyStraightMidrange = FALSE;
}
// If the mid range is straight (as determined above) then the
// DestinationBlackPoint shall be the same as initialLab.
// Otherwise, the DestinationBlackPoint shall be determined
// using curve fitting.
if (NearlyStraightMidrange) {
cmsLab2XYZ(NULL, BlackPoint, &InitialLab);
cmsDeleteTransform(hRoundTrip);
return TRUE;
}
}
// curve fitting: The round-trip curve normally looks like a nearly constant section at the black point,
// with a corner and a nearly straight line to the white point.
for (l=0; l < 256; l++) {
yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL);
}
// find the black point using the least squares error quadratic curve fitting
if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
lo = 0.1;
hi = 0.5;
}
else {
// Perceptual and saturation
lo = 0.03;
hi = 0.25;
}
// Capture shadow points for the fitting.
n = 0;
for (l=0; l < 256; l++) {
cmsFloat64Number ff = yRamp[l];
if (ff >= lo && ff < hi) {
x[n] = inRamp[l];
y[n] = yRamp[l];
n++;
}
}
// No suitable points
if (n < 3 ) {
cmsDeleteTransform(hRoundTrip);
BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE;
}
// fit and get the vertex of quadratic curve
Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y);
if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative
Lab.L = 0;
}
Lab.a = InitialLab.a;
Lab.b = InitialLab.b;
cmsLab2XYZ(NULL, BlackPoint, &Lab);
cmsDeleteTransform(hRoundTrip);
return TRUE;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2011 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"),
@ -170,10 +170,10 @@ 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;
if (*alpha >= SECTORS)
*alpha = SECTORS-1;
if (*theta >= SECTORS)
*theta = SECTORS-1;
}
@ -326,8 +326,8 @@ cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp)
// Housekeeping
_cmsAssert(gbd != NULL);
_cmsAssert(Lab != NULL);
_cmsAssert(sp != 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);
@ -439,7 +439,8 @@ static
int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[])
{
int nSectors = 0;
int i, a, t;
int a, t;
cmsUInt32Number i;
cmsGDBPoint* pt;
for (i=0; i < NSTEPS; i++) {
@ -476,7 +477,7 @@ cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta)
cmsVEC3 Centre;
cmsLine ray;
int nCloseSectors;
cmsGDBPoint* Close[NSTEPS];
cmsGDBPoint* Close[NSTEPS + 1];
cmsSpherical closel, templ;
cmsLine edge;
int k, m;
@ -553,13 +554,13 @@ cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags)
_cmsAssert(hGBD != NULL);
// Interpolate black
for (alpha = 0; alpha <= SECTORS; alpha++) {
for (alpha = 0; alpha < SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE;
}
// Interpolate white
for (alpha = 0; alpha <= SECTORS; alpha++) {
for (alpha = 0; alpha < SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE;
}
@ -567,7 +568,7 @@ cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags)
// Interpolate Mid
for (theta = 1; theta < SECTORS; theta++) {
for (alpha = 0; alpha <= SECTORS; alpha++) {
for (alpha = 0; alpha < SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE;
}
@ -708,10 +709,10 @@ cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname)
else
if (pt ->Type == GP_MODELED)
fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5);
else {
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");

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2014 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"),
@ -114,7 +114,7 @@ cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID,
if (!hICC) // can't allocate
return NULL;
cmsSetProfileVersion(hICC, 4.2);
cmsSetProfileVersion(hICC, 4.3);
cmsSetDeviceClass(hICC, cmsSigDisplayClass);
cmsSetColorSpace(hICC, cmsSigRgbData);
@ -179,9 +179,26 @@ cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID,
if (TransferFunction) {
// Tries to minimize space. Thanks to Richard Hughes for this nice idea
if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error;
if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
if (TransferFunction[1] == TransferFunction[0]) {
if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
} else {
if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
}
if (TransferFunction[2] == TransferFunction[0]) {
if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
} else {
if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
}
}
if (Primaries) {
@ -218,7 +235,7 @@ cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,
if (!hICC) // can't allocate
return NULL;
cmsSetProfileVersion(hICC, 4.2);
cmsSetProfileVersion(hICC, 4.3);
cmsSetDeviceClass(hICC, cmsSigDisplayClass);
cmsSetColorSpace(hICC, cmsSigGrayData);
@ -274,14 +291,13 @@ cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
{
cmsHPROFILE hICC;
cmsPipeline* Pipeline;
cmsStage* Lin;
int nChannels;
hICC = cmsCreateProfilePlaceholder(ContextID);
if (!hICC)
return NULL;
cmsSetProfileVersion(hICC, 4.2);
cmsSetProfileVersion(hICC, 4.3);
cmsSetDeviceClass(hICC, cmsSigLinkClass);
cmsSetColorSpace(hICC, ColorSpace);
@ -298,10 +314,8 @@ cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
// Copy tables to Pipeline
Lin = cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions);
if (Lin == NULL) goto Error;
cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, Lin);
if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
goto Error;
// Create tags
if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;
@ -315,6 +329,7 @@ cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
return hICC;
Error:
cmsPipelineFree(Pipeline);
if (hICC)
cmsCloseProfile(hICC);
@ -401,7 +416,7 @@ cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
if (!hICC) // can't allocate
return NULL;
cmsSetProfileVersion(hICC, 4.2);
cmsSetProfileVersion(hICC, 4.3);
cmsSetDeviceClass(hICC, cmsSigLinkClass);
cmsSetColorSpace(hICC, ColorSpace);
@ -422,9 +437,10 @@ cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels));
cmsPipelineInsertStage(LUT, cmsAT_END, CLUT);
cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels));
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
!cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||
!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
goto Error;
// Create tags
if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
@ -475,7 +491,8 @@ cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIE
LUT = cmsPipelineAlloc(ContextID, 3, 3);
if (LUT == NULL) goto Error;
cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3));
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
goto Error;
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
cmsPipelineFree(LUT);
@ -509,7 +526,7 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE
hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
if (hProfile == NULL) return NULL;
cmsSetProfileVersion(hProfile, 4.2);
cmsSetProfileVersion(hProfile, 4.3);
cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
cmsSetColorSpace(hProfile, cmsSigLabData);
@ -521,7 +538,8 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE
LUT = cmsPipelineAlloc(ContextID, 3, 3);
if (LUT == NULL) goto Error;
cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3));
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
goto Error;
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
cmsPipelineFree(LUT);
@ -554,7 +572,7 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
if (hProfile == NULL) return NULL;
cmsSetProfileVersion(hProfile, 4.2);
cmsSetProfileVersion(hProfile, 4.3);
cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
cmsSetColorSpace(hProfile, cmsSigXYZData);
@ -566,7 +584,8 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
LUT = cmsPipelineAlloc(ContextID, 3, 3);
if (LUT == NULL) goto Error;
cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3));
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
goto Error;
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
cmsPipelineFree(LUT);
@ -705,81 +724,83 @@ int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number O
// contrast, Saturation and white point displacement
cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
int nLUTPoints,
cmsFloat64Number Bright,
cmsFloat64Number Contrast,
cmsFloat64Number Hue,
cmsFloat64Number Saturation,
int TempSrc,
int TempDest)
int nLUTPoints,
cmsFloat64Number Bright,
cmsFloat64Number Contrast,
cmsFloat64Number Hue,
cmsFloat64Number Saturation,
int TempSrc,
int TempDest)
{
cmsHPROFILE hICC;
cmsPipeline* Pipeline;
BCHSWADJUSTS bchsw;
cmsCIExyY WhitePnt;
cmsStage* CLUT;
cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
int i;
cmsHPROFILE hICC;
cmsPipeline* Pipeline;
BCHSWADJUSTS bchsw;
cmsCIExyY WhitePnt;
cmsStage* CLUT;
cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
int i;
bchsw.Brightness = Bright;
bchsw.Contrast = Contrast;
bchsw.Hue = Hue;
bchsw.Saturation = Saturation;
cmsWhitePointFromTemp(&WhitePnt, TempSrc );
cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
cmsWhitePointFromTemp(&WhitePnt, TempDest);
cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
hICC = cmsCreateProfilePlaceholder(ContextID);
if (!hICC) // can't allocate
return NULL;
bchsw.Brightness = Bright;
bchsw.Contrast = Contrast;
bchsw.Hue = Hue;
bchsw.Saturation = Saturation;
cmsSetDeviceClass(hICC, cmsSigAbstractClass);
cmsSetColorSpace(hICC, cmsSigLabData);
cmsSetPCS(hICC, cmsSigLabData);
cmsWhitePointFromTemp(&WhitePnt, TempSrc );
cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
cmsWhitePointFromTemp(&WhitePnt, TempDest);
cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
// Creates a Pipeline with 3D grid only
Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
if (Pipeline == NULL) {
cmsCloseProfile(hICC);
return NULL;
}
hICC = cmsCreateProfilePlaceholder(ContextID);
if (!hICC) // can't allocate
return NULL;
for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
if (CLUT == NULL) return NULL;
cmsSetDeviceClass(hICC, cmsSigAbstractClass);
cmsSetColorSpace(hICC, cmsSigLabData);
cmsSetPCS(hICC, cmsSigLabData);
if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
// Shouldn't reach here
goto Error;
}
if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
goto Error;
}
// Creates a Pipeline with 3D grid only
Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
if (Pipeline == NULL) {
cmsCloseProfile(hICC);
return NULL;
}
// Create tags
if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
if (CLUT == NULL) return NULL;
cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
// Pipeline is already on virtual profile
cmsPipelineFree(Pipeline);
// Shouldn't reach here
cmsPipelineFree(Pipeline);
cmsCloseProfile(hICC);
return NULL;
}
// Ok, done
return hICC;
cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT);
// Create tags
if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
// Pipeline is already on virtual profile
cmsPipelineFree(Pipeline);
// Ok, done
return hICC;
Error:
cmsPipelineFree(Pipeline);
cmsCloseProfile(hICC);
return NULL;
}
@ -809,7 +830,7 @@ cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
if (!hProfile) // can't allocate
return NULL;
cmsSetProfileVersion(hProfile, 4.2);
cmsSetProfileVersion(hProfile, 4.3);
if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
@ -827,7 +848,8 @@ cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab);
cmsFreeToneCurve(EmptyTab);
cmsPipelineInsertStage(LUT, cmsAT_END, PostLin);
if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
goto Error;
if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
@ -934,6 +956,11 @@ cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
// Colorant count now depends on the output space
nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
// Make sure we have proper formatters
cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
| BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
// Apply the transfor to colorants.
for (i=0; i < nColors; i++) {
cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
@ -963,8 +990,9 @@ typedef struct {
static const cmsAllowedLUT AllowedLUTTypes[] = {
{ FALSE, 0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
{ FALSE, 0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
{ FALSE, 0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
{ FALSE, 0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType}},
{ TRUE , 0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType }},
{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
{ TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
@ -997,7 +1025,7 @@ cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
static
const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
{
int n;
cmsUInt32Number n;
for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
@ -1025,6 +1053,7 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
cmsContext ContextID = cmsGetTransformContextID(hTransform);
const cmsAllowedLUT* AllowedLUT;
cmsTagSignature DestinationTag;
cmsProfileClassSignature deviceClass;
_cmsAssert(hTransform != NULL);
@ -1046,13 +1075,15 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
// Time to fix the Lab2/Lab4 issue.
if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID));
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
goto Error;
}
// On the output side too
if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID));
if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
goto Error;
}
@ -1074,8 +1105,9 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
deviceClass = cmsGetDeviceClass(hProfile);
if (cmsGetDeviceClass(hProfile) == cmsSigOutputClass)
if (deviceClass == cmsSigOutputClass)
DestinationTag = cmsSigBToA0Tag;
else
DestinationTag = cmsSigAToB0Tag;
@ -1084,12 +1116,12 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
if (dwFlags & cmsFLAGS_FORCE_CLUT)
AllowedLUT = NULL;
else
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
if (AllowedLUT == NULL) {
// Try to optimize
_cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
_cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
}
@ -1098,14 +1130,16 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
if (AllowedLUT == NULL) {
dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
_cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
// Put identity curves if needed
if (cmsPipelineStageCount(LUT) == 1) {
if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType)
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
goto Error;
cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn));
cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut));
}
if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType)
if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
goto Error;
AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
}
@ -1134,10 +1168,22 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
}
if (xform ->Sequence != NULL) {
if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
}
// Set the white point
if (deviceClass == cmsSigInputClass) {
if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
}
else {
if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
}
// Per 7.2.15 in spec 4.3
cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
cmsPipelineFree(LUT);
return hProfile;

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2014 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"),
@ -47,48 +47,48 @@ const cmsCIExyY* CMSEXPORT cmsD50_xyY(void)
// Obtains WhitePoint from Temperature
cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK)
{
cmsFloat64Number x, y;
cmsFloat64Number T, T2, T3;
// cmsFloat64Number M1, M2;
cmsFloat64Number x, y;
cmsFloat64Number T, T2, T3;
// cmsFloat64Number M1, M2;
_cmsAssert(WhitePoint != NULL);
_cmsAssert(WhitePoint != NULL);
T = TempK;
T2 = T*T; // Square
T3 = T2*T; // Cube
T = TempK;
T2 = T*T; // Square
T3 = T2*T; // Cube
// For correlated color temperature (T) between 4000K and 7000K:
// For correlated color temperature (T) between 4000K and 7000K:
if (T >= 4000. && T <= 7000.)
{
x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
}
else
// or for correlated color temperature (T) between 7000K and 25000K:
if (T >= 4000. && T <= 7000.)
{
x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
}
else
// or for correlated color temperature (T) between 7000K and 25000K:
if (T > 7000.0 && T <= 25000.0)
{
x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
}
else {
cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp");
return FALSE;
}
if (T > 7000.0 && T <= 25000.0)
{
x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
}
else {
cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp");
return FALSE;
}
// Obtain y(x)
// Obtain y(x)
y = -3.000*(x*x) + 2.870*x - 0.275;
y = -3.000*(x*x) + 2.870*x - 0.275;
// wave factors (not used, but here for futures extensions)
// wave factors (not used, but here for futures extensions)
// M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
// M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
// 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);
WhitePoint -> x = x;
WhitePoint -> y = y;
WhitePoint -> Y = 1.0;
WhitePoint -> x = x;
WhitePoint -> y = y;
WhitePoint -> Y = 1.0;
return TRUE;
return TRUE;
}
@ -143,46 +143,46 @@ static ISOTEMPERATURE isotempdata[] = {
// Robertson's method
cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint)
{
int j;
cmsFloat64Number us,vs;
cmsFloat64Number uj,vj,tj,di,dj,mi,mj;
cmsFloat64Number xs, ys;
cmsUInt32Number j;
cmsFloat64Number us,vs;
cmsFloat64Number uj,vj,tj,di,dj,mi,mj;
cmsFloat64Number xs, ys;
_cmsAssert(WhitePoint != NULL);
_cmsAssert(WhitePoint != NULL);
_cmsAssert(TempK != NULL);
di = mi = 0;
xs = WhitePoint -> x;
ys = WhitePoint -> y;
di = mi = 0;
xs = WhitePoint -> x;
ys = WhitePoint -> y;
// convert (x,y) to CIE 1960 (u,WhitePoint)
// convert (x,y) to CIE 1960 (u,WhitePoint)
us = (2*xs) / (-xs + 6*ys + 1.5);
vs = (3*ys) / (-xs + 6*ys + 1.5);
us = (2*xs) / (-xs + 6*ys + 1.5);
vs = (3*ys) / (-xs + 6*ys + 1.5);
for (j=0; j < NISO; j++) {
for (j=0; j < NISO; j++) {
uj = isotempdata[j].ut;
vj = isotempdata[j].vt;
tj = isotempdata[j].tt;
mj = isotempdata[j].mirek;
uj = isotempdata[j].ut;
vj = isotempdata[j].vt;
tj = isotempdata[j].tt;
mj = isotempdata[j].mirek;
dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj);
dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj);
if ((j != 0) && (di/dj < 0.0)) {
if ((j != 0) && (di/dj < 0.0)) {
// Found a match
*TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
return TRUE;
}
// Found a match
*TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
return TRUE;
}
di = dj;
mi = mj;
}
di = dj;
mi = mj;
}
// Not found
return FALSE;
// Not found
return FALSE;
}
@ -226,41 +226,41 @@ cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion,
_cmsMAT3per(&Tmp, &Cone, Chad);
_cmsMAT3per(Conversion, &Chad_Inv, &Tmp);
return TRUE;
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 }}
}};
cmsMAT3 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;
if (ConeMatrix == NULL)
ConeMatrix = &LamRigg;
return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
}
// Same as anterior, but assuming D50 destination. White point is given in xyY
static
cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt)
{
cmsCIEXYZ Dn;
cmsMAT3 Bradford;
cmsMAT3 Tmp;
cmsCIEXYZ Dn;
cmsMAT3 Bradford;
cmsMAT3 Tmp;
cmsxyY2XYZ(&Dn, SourceWhitePt);
cmsxyY2XYZ(&Dn, SourceWhitePt);
if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE;
if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE;
Tmp = *r;
_cmsMAT3per(r, &Bradford, &Tmp);
Tmp = *r;
_cmsMAT3per(r, &Bradford, &Tmp);
return TRUE;
return TRUE;
}
// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
@ -278,45 +278,45 @@ cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt)
//
cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs)
{
cmsVEC3 WhitePoint, Coef;
cmsMAT3 Result, Primaries;
cmsFloat64Number xn, yn;
cmsFloat64Number xr, yr;
cmsFloat64Number xg, yg;
cmsFloat64Number xb, yb;
cmsVEC3 WhitePoint, Coef;
cmsMAT3 Result, Primaries;
cmsFloat64Number xn, yn;
cmsFloat64Number xr, yr;
cmsFloat64Number xg, yg;
cmsFloat64Number 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;
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
_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));
// 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));
// Result = Primaries ^ (-1) inverse matrix
if (!_cmsMAT3inverse(&Primaries, &Result))
return FALSE;
// Result = Primaries ^ (-1) inverse matrix
if (!_cmsMAT3inverse(&Primaries, &Result))
return FALSE;
_cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
_cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
// Across inverse primaries ...
_cmsMAT3eval(&Coef, &Result, &WhitePoint);
// 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));
// 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);
return _cmsAdaptMatrixToD50(r, WhitePt);
}
@ -324,28 +324,28 @@ cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, con
// 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)
const cmsCIEXYZ* SourceWhitePt,
const cmsCIEXYZ* Illuminant,
const cmsCIEXYZ* Value)
{
cmsMAT3 Bradford;
cmsVEC3 In, Out;
cmsMAT3 Bradford;
cmsVEC3 In, Out;
_cmsAssert(Result != NULL);
_cmsAssert(SourceWhitePt != NULL);
_cmsAssert(Illuminant != NULL);
_cmsAssert(Value != NULL);
_cmsAssert(Result != NULL);
_cmsAssert(SourceWhitePt != NULL);
_cmsAssert(Illuminant != NULL);
_cmsAssert(Value != NULL);
if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE;
if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE;
_cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z);
_cmsMAT3eval(&Out, &Bradford, &In);
_cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z);
_cmsMAT3eval(&Out, &Bradford, &In);
Result -> X = Out.n[0];
Result -> Y = Out.n[1];
Result -> Z = Out.n[2];
Result -> X = Out.n[0];
Result -> Y = Out.n[1];
Result -> Z = Out.n[2];
return TRUE;
return TRUE;
}

View File

@ -1,7 +1,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2014 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"),
@ -29,44 +29,120 @@
// Transformations stuff
// -----------------------------------------------------------------------
// Alarm codes for 16-bit transformations, because the fixed range of containers there are
// no values left to mark out of gamut. volatile is C99 per 6.2.5
static volatile cmsUInt16Number Alarm[cmsMAXCHANNELS];
static volatile cmsFloat64Number GlobalAdaptationState = 0;
#define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
// The Context0 observer adaptation state.
_cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
// Init and duplicate observer adaptation state
void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
void* from;
if (src != NULL) {
from = src ->chunks[AdaptationStateContext];
}
else {
from = &AdaptationStateChunk;
}
ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));
}
// Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all
// but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states.
cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d)
{
cmsFloat64Number prev;
_cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
// Get previous value for return
prev = ptr ->AdaptationState;
// Set the value if d is positive or zero
if (d >= 0.0) {
ptr ->AdaptationState = d;
}
// Always return previous value
return prev;
}
// The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
{
cmsFloat64Number OldVal = GlobalAdaptationState;
if (d >= 0)
GlobalAdaptationState = d;
return OldVal;
return cmsSetAdaptationStateTHR(NULL, d);
}
// Alarm codes are always global
void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
{
int i;
// -----------------------------------------------------------------------
// Alarm codes for 16-bit transformations, because the fixed range of containers there are
// no values left to mark out of gamut.
#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
_cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
// Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
// encoded in 16 bits.
void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
{
_cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
_cmsAssert(ContextAlarmCodes != NULL); // Can't happen
memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
}
// Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
// Values are meant to be encoded in 16 bits.
void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
{
_cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
_cmsAssert(ContextAlarmCodes != NULL); // Can't happen
memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
}
void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
{
_cmsAssert(NewAlarm != NULL);
for (i=0; i < cmsMAXCHANNELS; i++)
Alarm[i] = NewAlarm[i];
cmsSetAlarmCodesTHR(NULL, NewAlarm);
}
// You can get the codes cas well
void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
{
int i;
_cmsAssert(OldAlarm != NULL);
for (i=0; i < cmsMAXCHANNELS; i++)
OldAlarm[i] = Alarm[i];
cmsGetAlarmCodesTHR(NULL, OldAlarm);
}
// Init and duplicate alarm codes
void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
void* from;
if (src != NULL) {
from = src ->chunks[AlarmCodesContext];
}
else {
from = &AlarmCodesChunk;
}
ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
}
// -----------------------------------------------------------------------
// Get rid of transform resources
void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
{
@ -89,11 +165,13 @@ void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
if (p ->Sequence)
cmsFreeProfileSequenceDescription(p ->Sequence);
LCMS_FREE_LOCK(&p->rwlock);
if (p ->UserData)
p ->FreeUserData(p ->ContextID, p ->UserData);
_cmsFree(p ->ContextID, (void *) p);
}
// Apply transform
// Apply transform.
void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
const void* InputBuffer,
void* OutputBuffer,
@ -102,7 +180,20 @@ void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
{
_cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
p -> xform(p, InputBuffer, OutputBuffer, Size);
p -> xform(p, InputBuffer, OutputBuffer, Size, Size);
}
// Apply transform.
void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
const void* InputBuffer,
void* OutputBuffer,
cmsUInt32Number Size, cmsUInt32Number Stride)
{
_cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
p -> xform(p, InputBuffer, OutputBuffer, Size, Stride);
}
@ -113,7 +204,7 @@ void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
static
void FloatXFORM(_cmsTRANSFORM* p,
const void* in,
void* out, cmsUInt32Number Size)
void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{
cmsUInt8Number* accum;
cmsUInt8Number* output;
@ -126,7 +217,7 @@ void FloatXFORM(_cmsTRANSFORM* p,
for (i=0; i < Size; i++) {
accum = p -> FromInputFloat(p, fIn, accum, Size);
accum = p -> FromInputFloat(p, fIn, accum, Stride);
// Any gamut chack to do?
if (p ->GamutCheck != NULL) {
@ -154,7 +245,31 @@ void FloatXFORM(_cmsTRANSFORM* p,
}
// Back to asked representation
output = p -> ToOutputFloat(p, fOut, output, Size);
output = p -> ToOutputFloat(p, fOut, output, Stride);
}
}
static
void NullFloatXFORM(_cmsTRANSFORM* p,
const void* in,
void* out,
cmsUInt32Number Size,
cmsUInt32Number Stride)
{
cmsUInt8Number* accum;
cmsUInt8Number* output;
cmsFloat32Number fIn[cmsMAXCHANNELS];
cmsUInt32Number i, n;
accum = (cmsUInt8Number*) in;
output = (cmsUInt8Number*) out;
n = Size;
for (i=0; i < n; i++) {
accum = p -> FromInputFloat(p, fIn, accum, Stride);
output = p -> ToOutputFloat(p, fIn, output, Stride);
}
}
@ -164,7 +279,8 @@ void FloatXFORM(_cmsTRANSFORM* p,
static
void NullXFORM(_cmsTRANSFORM* p,
const void* in,
void* out, cmsUInt32Number Size)
void* out, cmsUInt32Number Size,
cmsUInt32Number Stride)
{
cmsUInt8Number* accum;
cmsUInt8Number* output;
@ -177,8 +293,8 @@ void NullXFORM(_cmsTRANSFORM* p,
for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size);
output = p -> ToOutput(p, wIn, output, Size);
accum = p -> FromInput(p, wIn, accum, Stride);
output = p -> ToOutput(p, wIn, output, Stride);
}
}
@ -187,7 +303,7 @@ void NullXFORM(_cmsTRANSFORM* p,
static
void PrecalculatedXFORM(_cmsTRANSFORM* p,
const void* in,
void* out, cmsUInt32Number Size)
void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{
register cmsUInt8Number* accum;
register cmsUInt8Number* output;
@ -200,14 +316,14 @@ void PrecalculatedXFORM(_cmsTRANSFORM* p,
for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size);
accum = p -> FromInput(p, wIn, accum, Stride);
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
output = p -> ToOutput(p, wOut, output, Size);
output = p -> ToOutput(p, wOut, output, Stride);
}
}
// Auxiliar: Handle precalculated gamut check
// Auxiliar: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
static
void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
const cmsUInt16Number wIn[],
@ -219,9 +335,12 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
if (wOutOfGamut >= 1) {
cmsUInt16Number i;
_cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
for (i=0; i < p ->Lut->OutputChannels; i++)
wOut[i] = Alarm[i];
for (i=0; i < p ->Lut->OutputChannels; i++) {
wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
}
}
else
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
@ -231,7 +350,7 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
static
void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
const void* in,
void* out, cmsUInt32Number Size)
void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{
cmsUInt8Number* accum;
cmsUInt8Number* output;
@ -244,9 +363,9 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size);
accum = p -> FromInput(p, wIn, accum, Stride);
TransformOnePixelWithGamutCheck(p, wIn, wOut);
output = p -> ToOutput(p, wOut, output, Size);
output = p -> ToOutput(p, wOut, output, Stride);
}
}
@ -255,13 +374,13 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
static
void CachedXFORM(_cmsTRANSFORM* p,
const void* in,
void* out, cmsUInt32Number Size)
void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{
cmsUInt8Number* accum;
cmsUInt8Number* output;
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
cmsUInt32Number i, n;
cmsUInt16Number CacheIn[cmsMAXCHANNELS], CacheOut[cmsMAXCHANNELS];
_cmsCACHE Cache;
accum = (cmsUInt8Number*) in;
output = (cmsUInt8Number*) out;
@ -271,36 +390,28 @@ void CachedXFORM(_cmsTRANSFORM* p,
memset(wIn, 0, sizeof(wIn));
memset(wOut, 0, sizeof(wOut));
LCMS_READ_LOCK(&p ->rwlock);
memmove(CacheIn, p ->CacheIn, sizeof(CacheIn));
memmove(CacheOut, p ->CacheOut, sizeof(CacheOut));
LCMS_UNLOCK(&p ->rwlock);
// Get copy of zero cache
memcpy(&Cache, &p ->Cache, sizeof(Cache));
for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size);
accum = p -> FromInput(p, wIn, accum, Stride);
if (memcmp(wIn, CacheIn, sizeof(CacheIn)) == 0) {
if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
memmove(wOut, CacheOut, sizeof(CacheOut));
memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
}
else {
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
memmove(CacheIn, wIn, sizeof(CacheIn));
memmove(CacheOut, wOut, sizeof(CacheOut));
memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
}
output = p -> ToOutput(p, wOut, output, Size);
output = p -> ToOutput(p, wOut, output, Stride);
}
LCMS_WRITE_LOCK(&p ->rwlock);
memmove(p->CacheIn, CacheIn, sizeof(CacheIn));
memmove(p->CacheOut, CacheOut, sizeof(CacheOut));
LCMS_UNLOCK(&p ->rwlock);
}
@ -308,13 +419,13 @@ void CachedXFORM(_cmsTRANSFORM* p,
static
void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
const void* in,
void* out, cmsUInt32Number Size)
void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{
cmsUInt8Number* accum;
cmsUInt8Number* output;
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
cmsUInt32Number i, n;
cmsUInt16Number CacheIn[cmsMAXCHANNELS], CacheOut[cmsMAXCHANNELS];
_cmsCACHE Cache;
accum = (cmsUInt8Number*) in;
output = (cmsUInt8Number*) out;
@ -324,52 +435,211 @@ void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
LCMS_READ_LOCK(&p ->rwlock);
memmove(CacheIn, p ->CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
memmove(CacheOut, p ->CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
LCMS_UNLOCK(&p ->rwlock);
// Get copy of zero cache
memcpy(&Cache, &p ->Cache, sizeof(Cache));
for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size);
accum = p -> FromInput(p, wIn, accum, Stride);
if (memcmp(wIn, CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS) == 0) {
memmove(wOut, CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
}
else {
TransformOnePixelWithGamutCheck(p, wIn, wOut);
memmove(CacheIn, wIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
memmove(CacheOut, wOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
}
output = p -> ToOutput(p, wOut, output, Size);
output = p -> ToOutput(p, wOut, output, Stride);
}
LCMS_WRITE_LOCK(&p ->rwlock);
memmove(p->CacheIn, CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
memmove(p->CacheOut, CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
LCMS_UNLOCK(&p ->rwlock);
}
// -------------------------------------------------------------------------------------------------------------
// List of used-defined transform factories
typedef struct _cmsTransformCollection_st {
_cmsTransformFactory Factory;
struct _cmsTransformCollection_st *Next;
} _cmsTransformCollection;
// The linked list head
_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
// Duplicates the zone of memory used by the plug-in in the new context
static
void DupPluginTransformList(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
_cmsTransformPluginChunkType newHead = { NULL };
_cmsTransformCollection* entry;
_cmsTransformCollection* Anterior = NULL;
_cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
// Walk the list copying all nodes
for (entry = head->TransformCollection;
entry != NULL;
entry = entry ->Next) {
_cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
if (newEntry == NULL)
return;
// We want to keep the linked list order, so this is a little bit tricky
newEntry -> Next = NULL;
if (Anterior)
Anterior -> Next = newEntry;
Anterior = newEntry;
if (newHead.TransformCollection == NULL)
newHead.TransformCollection = newEntry;
}
ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
}
void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src)
{
if (src != NULL) {
// Copy all linked list
DupPluginTransformList(ctx, src);
}
else {
static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
}
}
// Allocate transform struct and set it to defaults
static
_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
// Register new ways to transform
cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
{
cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
_cmsTransformCollection* fl;
_cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
if (Data == NULL) {
// Free the chain. Memory is safely freed at exit
ctx->TransformCollection = NULL;
return TRUE;
}
// Factory callback is required
if (Plugin ->Factory == NULL) return FALSE;
fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
if (fl == NULL) return FALSE;
// Copy the parameters
fl ->Factory = Plugin ->Factory;
// Keep linked list
fl ->Next = ctx->TransformCollection;
ctx->TransformCollection = fl;
// All is ok
return TRUE;
}
void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
{
_cmsAssert(CMMcargo != NULL);
CMMcargo ->UserData = ptr;
CMMcargo ->FreeUserData = FreePrivateDataFn;
}
// returns the pointer defined by the plug-in to store private data
void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
{
_cmsAssert(CMMcargo != NULL);
return CMMcargo ->UserData;
}
// returns the current formatters
void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
{
_cmsAssert(CMMcargo != NULL);
if (FromInput) *FromInput = CMMcargo ->FromInput;
if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
}
void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
{
_cmsAssert(CMMcargo != NULL);
if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
}
// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
// for separated transforms. If this is the case,
static
_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
{
_cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
_cmsTransformCollection* Plugin;
// Allocate needed memory
_cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
if (!p) return NULL;
// Store the proposed pipeline
p ->Lut = lut;
// Let's see if any plug-in want to do the transform by itself
for (Plugin = ctx ->TransformCollection;
Plugin != NULL;
Plugin = Plugin ->Next) {
if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) {
// Last plugin in the declaration order takes control. We just keep
// the original parameters as a logging.
// Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
// an optimized transform is not reusable. The plug-in can, however, change
// the flags and make it suitable.
p ->ContextID = ContextID;
p ->InputFormat = *InputFormat;
p ->OutputFormat = *OutputFormat;
p ->dwOriginalFlags = *dwFlags;
// Fill the formatters just in case the optimized routine is interested.
// No error is thrown if the formatter doesn't exist. It is up to the optimization
// factory to decide what to do in those cases.
p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
return p;
}
}
// Not suitable for the transform plug-in, let's check the pipeline plug-in
if (p ->Lut != NULL)
_cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
// Check whatever this is a true floating point transform
if (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat)) {
if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
// Get formatter function always return a valid union, but the contents of this union may be NULL.
p ->FromInputFloat = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
p ->ToOutputFloat = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
*dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
@ -378,21 +648,28 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
return NULL;
}
// Float transforms don't use caché, always are non-NULL
p ->xform = FloatXFORM;
if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
p ->xform = NullFloatXFORM;
}
else {
// Float transforms don't use caché, always are non-NULL
p ->xform = FloatXFORM;
}
}
else {
if (InputFormat == 0 && OutputFormat == 0) {
if (*InputFormat == 0 && *OutputFormat == 0) {
p ->FromInput = p ->ToOutput = NULL;
*dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
}
else {
int BytesPerPixelInput;
p ->FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
p ->ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
if (p ->FromInput == NULL || p ->ToOutput == NULL) {
@ -403,25 +680,25 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
BytesPerPixelInput = T_BYTES(p ->InputFormat);
if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
*dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
}
if (dwFlags & cmsFLAGS_NULLTRANSFORM) {
if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
p ->xform = NullXFORM;
}
else {
if (dwFlags & cmsFLAGS_NOCACHE) {
if (*dwFlags & cmsFLAGS_NOCACHE) {
if (dwFlags & cmsFLAGS_GAMUTCHECK)
if (*dwFlags & cmsFLAGS_GAMUTCHECK)
p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no caché
else
p ->xform = PrecalculatedXFORM; // No caché, no gamut check
}
else {
if (dwFlags & cmsFLAGS_GAMUTCHECK)
if (*dwFlags & cmsFLAGS_GAMUTCHECK)
p ->xform = CachedXFORMGamutCheck; // Gamut check, caché
else
p ->xform = CachedXFORM; // No gamut check, caché
@ -430,14 +707,11 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
}
}
// Create a mutex for shared memory
LCMS_CREATE_LOCK(&p->rwlock);
p ->InputFormat = InputFormat;
p ->OutputFormat = OutputFormat;
p ->dwOriginalFlags = dwFlags;
p ->InputFormat = *InputFormat;
p ->OutputFormat = *OutputFormat;
p ->dwOriginalFlags = *dwFlags;
p ->ContextID = ContextID;
p ->UserData = NULL;
return p;
}
@ -448,36 +722,49 @@ cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpac
cmsColorSpaceSignature PostColorSpace;
int i;
if (hProfiles[0] == NULL) return FALSE;
if (nProfiles <= 0) return FALSE;
if (hProfiles[0] == NULL) return FALSE;
*Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
for (i=0; i < nProfiles; i++) {
cmsProfileClassSignature cls;
cmsHPROFILE hProfile = hProfiles[i];
int lIsInput = (PostColorSpace != cmsSigXYZData) &&
(PostColorSpace != cmsSigLabData);
if (hProfile == NULL) return FALSE;
if (hProfile == NULL) return FALSE;
if (lIsInput) {
cls = cmsGetDeviceClass(hProfile);
if (cls == cmsSigNamedColorClass) {
ColorSpaceIn = cmsSig1colorData;
ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
}
else
if (lIsInput || (cls == cmsSigLinkClass)) {
ColorSpaceIn = cmsGetColorSpace(hProfile);
ColorSpaceOut = cmsGetPCS(hProfile);
}
else {
else
{
ColorSpaceIn = cmsGetPCS(hProfile);
ColorSpaceOut = cmsGetColorSpace(hProfile);
}
if (i==0)
*Input = ColorSpaceIn;
PostColorSpace = ColorSpaceOut;
}
*Output = PostColorSpace;
return TRUE;
return TRUE;
}
// Check colorspace
@ -498,6 +785,22 @@ cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwForm
// ----------------------------------------------------------------------------------------------------------------
static
void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
{
if (src == NULL) {
wtPt ->X = cmsD50X;
wtPt ->Y = cmsD50Y;
wtPt ->Z = cmsD50Z;
}
else {
wtPt ->X = src->X;
wtPt ->Y = src->Y;
wtPt ->Z = src->Z;
}
}
// New to lcms 2.0 -- have all parameters available.
cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
@ -511,28 +814,31 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
cmsUInt32Number dwFlags)
{
_cmsTRANSFORM* xform;
cmsBool FloatTransform;
cmsColorSpaceSignature EntryColorSpace;
cmsColorSpaceSignature ExitColorSpace;
cmsPipeline* Lut;
cmsUInt32Number LastIntent = Intents[nProfiles-1];
// If it is a fake transform
if (dwFlags & cmsFLAGS_NULLTRANSFORM)
{
return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
}
// If gamut check is requested, make sure we have a gamut profile
if (dwFlags & cmsFLAGS_GAMUTCHECK) {
if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
}
// On floating point transforms, inhibit optimizations
FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat));
// On floating point transforms, inhibit cache
if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
dwFlags |= cmsFLAGS_NOCACHE;
// Mark entry/exit spaces
if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
return NULL;
}
if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
return NULL;
}
// Check if proper colorspaces
if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
@ -552,21 +858,29 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
return NULL;
}
// Optimize the LUT if possible
_cmsOptimizePipeline(&Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
// Check channel count
if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
(cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) {
cmsPipelineFree(Lut);
cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
return NULL;
}
// All seems ok
xform = AllocEmptyTransform(ContextID, InputFormat, OutputFormat, dwFlags);
xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
if (xform == NULL) {
cmsPipelineFree(Lut);
return NULL;
}
// Keep values
xform ->EntryColorSpace = EntryColorSpace;
xform ->ExitColorSpace = ExitColorSpace;
xform ->Lut = Lut;
xform ->RenderingIntent = Intents[nProfiles-1];
// Take white points
SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
// Create a gamut check LUT if requested
@ -613,14 +927,14 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
// If this is a cached transform, init first value, which is zero (16 bits only)
if (!(dwFlags & cmsFLAGS_NOCACHE)) {
memset(&xform ->CacheIn, 0, sizeof(xform ->CacheIn));
memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
if (xform ->GamutCheck != NULL) {
TransformOnePixelWithGamutCheck(xform, xform ->CacheIn, xform->CacheOut);
TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
}
else {
xform ->Lut ->Eval16Fn(xform ->CacheIn, xform->CacheOut, xform -> Lut->Data);
xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
}
}
@ -629,7 +943,6 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
}
// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
cmsHPROFILE hProfiles[],
cmsUInt32Number nProfiles,
@ -651,7 +964,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
for (i=0; i < nProfiles; i++) {
BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
Intents[i] = Intent;
AdaptationStates[i] = GlobalAdaptationState;
AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
}
@ -731,7 +1044,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = GlobalAdaptationState;
Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
@ -772,7 +1085,22 @@ cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
return xform -> ContextID;
}
// Grab the input/output formats
cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
{
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
if (xform == NULL) return 0;
return xform->InputFormat;
}
cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
{
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
if (xform == NULL) return 0;
return xform->OutputFormat;
}
// For backwards compatibility
cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
@ -782,18 +1110,17 @@ cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
cmsFormatter16 FromInput, ToOutput;
cmsUInt32Number BytesPerPixelInput;
// We only can afford to change formatters if previous transform is at least 16 bits
BytesPerPixelInput = T_BYTES(xform ->InputFormat);
if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
return FALSE;
}
FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
if (FromInput == NULL || ToOutput == NULL) {
@ -803,7 +1130,7 @@ cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
xform ->InputFormat = InputFormat;
xform ->OutputFormat = OutputFormat;
xform ->FromInput = FromInput;
xform ->ToOutput = ToOutput;
xform ->FromInput = FromInput;
xform ->ToOutput = ToOutput;
return TRUE;
}

View File

@ -63,9 +63,11 @@ _cmsDefaultICCintents = _cmsDefaultICCintents
cmsDeleteTransform = cmsDeleteTransform
cmsDeltaE = cmsDeltaE
cmsDetectBlackPoint = cmsDetectBlackPoint
cmsDetectDestinationBlackPoint = cmsDetectDestinationBlackPoint
cmsDetectTAC = cmsDetectTAC
cmsDesaturateLab = cmsDesaturateLab
cmsDoTransform = cmsDoTransform
cmsDoTransformStride = cmsDoTransformStride
_cmsDoubleTo15Fixed16 = _cmsDoubleTo15Fixed16
_cmsDoubleTo8Fixed8 = _cmsDoubleTo8Fixed8
_cmsDupMem = _cmsDupMem
@ -74,6 +76,8 @@ cmsDupProfileSequenceDescription = cmsDupProfileSequenceDescription
cmsDupToneCurve = cmsDupToneCurve
_cmsEncodeDateTimeNumber = _cmsEncodeDateTimeNumber
cmsEstimateGamma = cmsEstimateGamma
cmsGetToneCurveEstimatedTableEntries = cmsGetToneCurveEstimatedTableEntries
cmsGetToneCurveEstimatedTable = cmsGetToneCurveEstimatedTable
cmsEvalToneCurve16 = cmsEvalToneCurve16
cmsEvalToneCurveFloat = cmsEvalToneCurveFloat
cmsfilelength = cmsfilelength
@ -110,12 +114,12 @@ cmsGetPostScriptCRD = cmsGetPostScriptCRD
cmsGetPostScriptCSA = cmsGetPostScriptCSA
cmsGetProfileInfo = cmsGetProfileInfo
cmsGetProfileInfoASCII = cmsGetProfileInfoASCII
cmsGetProfileContextID = cmsGetProfileContextID
cmsGetProfileContextID = cmsGetProfileContextID
cmsGetProfileVersion = cmsGetProfileVersion
cmsGetSupportedIntents = cmsGetSupportedIntents
cmsGetTagCount = cmsGetTagCount
cmsGetTagSignature = cmsGetTagSignature
cmsGetTransformContextID = cmsGetTransformContextID
cmsGetTransformContextID = cmsGetTransformContextID
_cmsICCcolorSpace = _cmsICCcolorSpace
_cmsIOPrintf = _cmsIOPrintf
cmsIsCLUT = cmsIsCLUT
@ -131,15 +135,18 @@ cmsIT8Alloc = cmsIT8Alloc
cmsIT8DefineDblFormat = cmsIT8DefineDblFormat
cmsIT8EnumDataFormat = cmsIT8EnumDataFormat
cmsIT8EnumProperties = cmsIT8EnumProperties
cmsIT8EnumPropertyMulti = cmsIT8EnumPropertyMulti
cmsIT8Free = cmsIT8Free
cmsIT8GetData = cmsIT8GetData
cmsIT8GetDataDbl = cmsIT8GetDataDbl
cmsIT8FindDataFormat = cmsIT8FindDataFormat
cmsIT8FindDataFormat = cmsIT8FindDataFormat
cmsIT8GetDataRowCol = cmsIT8GetDataRowCol
cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl
cmsIT8GetPatchName = cmsIT8GetPatchName
cmsIT8GetPatchByName = cmsIT8GetPatchByName
cmsIT8GetProperty = cmsIT8GetProperty
cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl
cmsIT8GetPropertyMulti = cmsIT8GetPropertyMulti
cmsIT8GetSheetType = cmsIT8GetSheetType
cmsIT8LoadFromFile = cmsIT8LoadFromFile
cmsIT8LoadFromMem = cmsIT8LoadFromMem
@ -154,10 +161,12 @@ cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl
cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl
cmsIT8SetPropertyHex = cmsIT8SetPropertyHex
cmsIT8SetPropertyStr = cmsIT8SetPropertyStr
cmsIT8SetPropertyMulti = cmsIT8SetPropertyMulti
cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked
cmsIT8SetSheetType = cmsIT8SetSheetType
cmsIT8SetTable = cmsIT8SetTable
cmsIT8SetTableByLabel = cmsIT8SetTableByLabel
cmsIT8SetIndexColumn = cmsIT8SetIndexColumn
cmsIT8TableCount = cmsIT8TableCount
cmsJoinToneCurve = cmsJoinToneCurve
cmsLab2LCh = cmsLab2LCh
@ -167,6 +176,7 @@ cmsLabEncoded2FloatV2 = cmsLabEncoded2FloatV2
cmsLCh2Lab = cmsLCh2Lab
_cmsLCMScolorSpace = _cmsLCMScolorSpace
cmsLinkTag = cmsLinkTag
cmsTagLinkedTo = cmsTagLinkedTo
cmsPipelineAlloc = cmsPipelineAlloc
cmsPipelineCat = cmsPipelineCat
cmsPipelineCheckAndRetreiveStages = cmsPipelineCheckAndRetreiveStages
@ -291,10 +301,41 @@ _cmsWriteUInt32Number = _cmsWriteUInt32Number
_cmsWriteUInt64Number = _cmsWriteUInt64Number
_cmsWriteUInt8Number = _cmsWriteUInt8Number
_cmsWriteXYZNumber = _cmsWriteXYZNumber
cmsxyY2XYZ = cmsxyY2XYZ
cmsXYZ2Lab = cmsXYZ2Lab
cmsxyY2XYZ = cmsxyY2XYZ
cmsXYZ2Lab = cmsXYZ2Lab
cmsXYZ2xyY = cmsXYZ2xyY
cmsXYZEncoded2Float = cmsXYZEncoded2Float
cmsSliceSpace16 = cmsSliceSpace16
cmsSliceSpaceFloat = cmsSliceSpaceFloat
cmsChangeBuffersFormat = cmsChangeBuffersFormat
cmsDictAlloc = cmsDictAlloc
cmsDictFree = cmsDictFree
cmsDictDup = cmsDictDup
cmsDictAddEntry = cmsDictAddEntry
cmsDictGetEntryList = cmsDictGetEntryList
cmsDictNextEntry = cmsDictNextEntry
_cmsGetTransformUserData = _cmsGetTransformUserData
_cmsSetTransformUserData = _cmsSetTransformUserData
_cmsGetTransformFormatters16 = _cmsGetTransformFormatters16
_cmsGetTransformFormattersFloat = _cmsGetTransformFormattersFloat
cmsGetHeaderCreator = cmsGetHeaderCreator
cmsPluginTHR = cmsPluginTHR
cmsGetPipelineContextID = cmsGetPipelineContextID
cmsGetTransformInputFormat = cmsGetTransformInputFormat
cmsGetTransformOutputFormat = cmsGetTransformOutputFormat
cmsCreateContext = cmsCreateContext
cmsDupContext = cmsDupContext
cmsDeleteContext = cmsDeleteContext
cmsGetContextUserData = cmsGetContextUserData
cmsUnregisterPluginsTHR = cmsUnregisterPluginsTHR
cmsSetAlarmCodesTHR = cmsSetAlarmCodesTHR
cmsGetAlarmCodesTHR = cmsGetAlarmCodesTHR
cmsSetAdaptationStateTHR = cmsSetAdaptationStateTHR
cmsSetLogErrorHandlerTHR = cmsSetLogErrorHandlerTHR
cmsGetSupportedIntentsTHR = cmsGetSupportedIntentsTHR
cmsMLUtranslationsCount = cmsMLUtranslationsCount
cmsMLUtranslationsCodes = cmsMLUtranslationsCodes
_cmsCreateMutex = _cmsCreateMutex
_cmsDestroyMutex = _cmsDestroyMutex
_cmsLockMutex = _cmsLockMutex
_cmsUnlockMutex = _cmsUnlockMutex

View File

@ -1,7 +1,7 @@
//
// Little Color Management System
// Copyright (c) 1998-2010 Marti Maria Saguer
// Copyright (c) 1998-2014 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"),
@ -46,15 +46,18 @@
# define M_LOG10E 0.434294481903251827651
#endif
// BorlandC 5.5 & Visual Studio 2003 are broken on that
#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1310))
// BorlandC 5.5, VC2003 are broken on that
#if defined(__BORLANDC__) || (_MSC_VER < 1400) // 1400 == VC++ 8.0
#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))
#define _cmsALIGNLONG(x) (((x)+(sizeof(cmsUInt32Number)-1)) & ~(sizeof(cmsUInt32Number)-1))
// Alignment to memory pointer
#define _cmsALIGNMEM(x) (((x)+(sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
// Maximum encodeable values in floating point
#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0)
@ -64,7 +67,7 @@
#define MAX_ENCODEABLE_ab4 (127.0)
// Maximum of channels for internal pipeline evaluation
#define MAX_STAGE_CHANNELS 128
#define MAX_STAGE_CHANNELS 128
// Unused parameter warning supression
#define cmsUNUSED_PARAMETER(x) ((void)x)
@ -87,36 +90,6 @@
# 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))
@ -191,42 +164,202 @@ cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d)
return _cmsQuickFloorWord(d);
}
// Plug-In registering ---------------------------------------------------------------
// Pthread support --------------------------------------------------------------------
#ifndef CMS_NO_PTHREADS
// This is the threading support. Unfortunately, it has to be platform-dependent because
// windows does not support pthreads.
#ifdef CMS_IS_WINDOWS_
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
// From: http://locklessinc.com/articles/pthreads_on_windows/
// The pthreads API has an initialization macro that has no correspondence to anything in
// the windows API. By investigating the internal definition of the critical section type,
// one may work out how to initialize one without calling InitializeCriticalSection().
// The trick here is that InitializeCriticalSection() is not allowed to fail. It tries
// to allocate a critical section debug object, but if no memory is available, it sets
// the pointer to a specific value. (One would expect that value to be NULL, but it is
// actually (void *)-1 for some reason.) Thus we can use this special value for that
// pointer, and the critical section code will work.
// The other important part of the critical section type to initialize is the number
// of waiters. This controls whether or not the mutex is locked. Fortunately, this
// part of the critical section is unlikely to change. Apparently, many programs
// already test critical sections to see if they are locked using this value, so
// Microsoft felt that it was necessary to keep it set at -1 for an unlocked critical
// section, even when they changed the underlying algorithm to be more scalable.
// The final parts of the critical section object are unimportant, and can be set
// to zero for their defaults. This yields an initialization macro:
typedef CRITICAL_SECTION _cmsMutex;
#define CMS_MUTEX_INITIALIZER {(void*) -1,-1,0,0,0,0}
cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
{
EnterCriticalSection(m);
return 0;
}
cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
{
LeaveCriticalSection(m);
return 0;
}
cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
{
InitializeCriticalSection(m);
return 0;
}
cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
{
DeleteCriticalSection(m);
return 0;
}
cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
{
EnterCriticalSection(m);
return 0;
}
cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
{
LeaveCriticalSection(m);
return 0;
}
#else
// Rest of the wide world
#include <pthread.h>
#define CMS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
typedef pthread_mutex_t _cmsMutex;
cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
{
return pthread_mutex_lock(m);
}
cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
{
return pthread_mutex_unlock(m);
}
cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
{
return pthread_mutex_init(m, NULL);
}
cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
{
return pthread_mutex_destroy(m);
}
cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
{
return pthread_mutex_lock(m);
}
cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
{
return pthread_mutex_unlock(m);
}
#endif
#else
#define CMS_MUTEX_INITIALIZER 0
typedef int _cmsMutex;
cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
{
return 0;
cmsUNUSED_PARAMETER(m);
}
cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
{
return 0;
cmsUNUSED_PARAMETER(m);
}
cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
{
return 0;
cmsUNUSED_PARAMETER(m);
}
cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
{
return 0;
cmsUNUSED_PARAMETER(m);
}
cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
{
return 0;
cmsUNUSED_PARAMETER(m);
}
cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
{
return 0;
cmsUNUSED_PARAMETER(m);
}
#endif
// Plug-In registration ---------------------------------------------------------------
// Specialized function for plug-in memory management. No pairing free() since whole pool is freed at once.
void* _cmsPluginMalloc(cmsUInt32Number size);
void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size);
// Memory management
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Interpolation
cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Parametric curves
cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Formatters management
cmsBool _cmsRegisterFormattersPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Tag type management
cmsBool _cmsRegisterTagTypePlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Tag management
cmsBool _cmsRegisterTagPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterTagPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Intent management
cmsBool _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Multi Process elements
cmsBool _cmsRegisterMultiProcessElementPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Optimization
cmsBool _cmsRegisterOptimizationPlugin(cmsPluginBase* Plugin);
cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Transform
cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Mutex
cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// ---------------------------------------------------------------------------------------------------------
// Suballocators. Those are blocks of memory that is freed at the end on whole block.
// Suballocators.
typedef struct _cmsSubAllocator_chunk_st {
cmsUInt8Number* Block;
@ -249,9 +382,264 @@ typedef struct {
_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial);
void _cmsSubAllocDestroy(_cmsSubAllocator* s);
void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size);
void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size);
// ----------------------------------------------------------------------------------
// The context clients.
typedef enum {
UserPtr, // User-defined pointer
Logger,
AlarmCodesContext,
AdaptationStateContext,
MemPlugin,
InterpPlugin,
CurvesPlugin,
FormattersPlugin,
TagTypePlugin,
TagPlugin,
IntentPlugin,
MPEPlugin,
OptimizationPlugin,
TransformPlugin,
MutexPlugin,
// Last in list
MemoryClientMax
} _cmsMemoryClient;
// Container for memory management plug-in.
typedef struct {
_cmsMallocFnPtrType MallocPtr;
_cmsMalloZerocFnPtrType MallocZeroPtr;
_cmsFreeFnPtrType FreePtr;
_cmsReallocFnPtrType ReallocPtr;
_cmsCallocFnPtrType CallocPtr;
_cmsDupFnPtrType DupPtr;
} _cmsMemPluginChunkType;
// Copy memory management function pointers from plug-in to chunk, taking care of missing routines
void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr);
// Internal structure for context
struct _cmsContext_struct {
struct _cmsContext_struct* Next; // Points to next context in the new style
_cmsSubAllocator* MemPool; // The memory pool that stores context data
void* chunks[MemoryClientMax]; // array of pointers to client chunks. Memory itself is hold in the suballocator.
// If NULL, then it reverts to global Context0
_cmsMemPluginChunkType DefaultMemoryManager; // The allocators used for creating the context itself. Cannot be overriden
};
// Returns a pointer to a valid context structure, including the global one if id is zero.
// Verifies the magic number.
struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID);
// Returns the block assigned to the specific zone.
void* _cmsContextGetClientChunk(cmsContext id, _cmsMemoryClient mc);
// Chunks of context memory by plug-in client -------------------------------------------------------
// Those structures encapsulates all variables needed by the several context clients (mostly plug-ins)
// Container for error logger -- not a plug-in
typedef struct {
cmsLogErrorHandlerFunction LogErrorHandler; // Set to NULL for Context0 fallback
} _cmsLogErrorChunkType;
// The global Context0 storage for error logger
extern _cmsLogErrorChunkType _cmsLogErrorChunk;
// Allocate and init error logger container.
void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for alarm codes -- not a plug-in
typedef struct {
cmsUInt16Number AlarmCodes[cmsMAXCHANNELS];
} _cmsAlarmCodesChunkType;
// The global Context0 storage for alarm codes
extern _cmsAlarmCodesChunkType _cmsAlarmCodesChunk;
// Allocate and init alarm codes container.
void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for adaptation state -- not a plug-in
typedef struct {
cmsFloat64Number AdaptationState;
} _cmsAdaptationStateChunkType;
// The global Context0 storage for adaptation state
extern _cmsAdaptationStateChunkType _cmsAdaptationStateChunk;
// Allocate and init adaptation state container.
void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// The global Context0 storage for memory management
extern _cmsMemPluginChunkType _cmsMemPluginChunk;
// Allocate and init memory management container.
void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for interpolation plug-in
typedef struct {
cmsInterpFnFactory Interpolators;
} _cmsInterpPluginChunkType;
// The global Context0 storage for interpolation plug-in
extern _cmsInterpPluginChunkType _cmsInterpPluginChunk;
// Allocate and init interpolation container.
void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for parametric curves plug-in
typedef struct {
struct _cmsParametricCurvesCollection_st* ParametricCurves;
} _cmsCurvesPluginChunkType;
// The global Context0 storage for tone curves plug-in
extern _cmsCurvesPluginChunkType _cmsCurvesPluginChunk;
// Allocate and init parametric curves container.
void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for formatters plug-in
typedef struct {
struct _cms_formatters_factory_list* FactoryList;
} _cmsFormattersPluginChunkType;
// The global Context0 storage for formatters plug-in
extern _cmsFormattersPluginChunkType _cmsFormattersPluginChunk;
// Allocate and init formatters container.
void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// This chunk type is shared by TagType plug-in and MPE Plug-in
typedef struct {
struct _cmsTagTypeLinkedList_st* TagTypes;
} _cmsTagTypePluginChunkType;
// The global Context0 storage for tag types plug-in
extern _cmsTagTypePluginChunkType _cmsTagTypePluginChunk;
// The global Context0 storage for mult process elements plug-in
extern _cmsTagTypePluginChunkType _cmsMPETypePluginChunk;
// Allocate and init Tag types container.
void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Allocate and init MPE container.
void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for tag plug-in
typedef struct {
struct _cmsTagLinkedList_st* Tag;
} _cmsTagPluginChunkType;
// The global Context0 storage for tag plug-in
extern _cmsTagPluginChunkType _cmsTagPluginChunk;
// Allocate and init Tag container.
void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for intents plug-in
typedef struct {
struct _cms_intents_list* Intents;
} _cmsIntentsPluginChunkType;
// The global Context0 storage for intents plug-in
extern _cmsIntentsPluginChunkType _cmsIntentsPluginChunk;
// Allocate and init intents container.
void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for optimization plug-in
typedef struct {
struct _cmsOptimizationCollection_st* OptimizationCollection;
} _cmsOptimizationPluginChunkType;
// The global Context0 storage for optimizers plug-in
extern _cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk;
// Allocate and init optimizers container.
void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for transform plug-in
typedef struct {
struct _cmsTransformCollection_st* TransformCollection;
} _cmsTransformPluginChunkType;
// The global Context0 storage for full-transform replacement plug-in
extern _cmsTransformPluginChunkType _cmsTransformPluginChunk;
// Allocate and init transform container.
void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// Container for mutex plug-in
typedef struct {
_cmsCreateMutexFnPtrType CreateMutexPtr;
_cmsDestroyMutexFnPtrType DestroyMutexPtr;
_cmsLockMutexFnPtrType LockMutexPtr;
_cmsUnlockMutexFnPtrType UnlockMutexPtr;
} _cmsMutexPluginChunkType;
// The global Context0 storage for mutex plug-in
extern _cmsMutexPluginChunkType _cmsMutexPluginChunk;
// Allocate and init mutex container.
void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx,
const struct _cmsContext_struct* src);
// ----------------------------------------------------------------------------------
// MLU internal representation
typedef struct {
@ -259,7 +647,7 @@ typedef struct {
cmsUInt16Number Country;
cmsUInt32Number StrW; // Offset to current unicode string
cmsUInt32Number Len; // Lenght in bytes
cmsUInt32Number Len; // Length in bytes
} _cmsMLUentry;
@ -326,9 +714,11 @@ typedef struct _cms_iccprofile_struct {
cmsColorSpaceSignature ColorSpace;
cmsColorSpaceSignature PCS;
cmsUInt32Number RenderingIntent;
cmsUInt32Number flags;
cmsUInt32Number manufacturer, model;
cmsUInt64Number attributes;
cmsUInt32Number creator;
cmsProfileID ProfileID;
@ -341,10 +731,14 @@ typedef struct _cms_iccprofile_struct {
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.
// depending on profile version, so we keep track of the
// type handler for each tag in the list.
// Special
cmsBool IsWrite;
// Keep a mutex for cmsReadTag -- Note that this only works if the user includes a mutex plugin
void * UsrMutex;
} _cmsICCPROFILE;
// IO helpers for profiles
@ -353,9 +747,9 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSp
int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks);
// Tag types
cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsTagTypeSignature sig);
cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig);
cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig);
cmsTagDescriptor* _cmsGetTagDescriptor(cmsTagSignature sig);
cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig);
// Error logging ---------------------------------------------------------------------------------------------------------
@ -366,7 +760,7 @@ void _cmsTagSignature2String(char String[5], cmsTagSignature sig
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);
cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p);
cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p);
// Curves ----------------------------------------------------------------------------------------------------------------
@ -414,37 +808,6 @@ struct _cmsStage_struct {
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);
@ -453,9 +816,13 @@ cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID);
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList);
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS);
cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels);
cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan);
cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID);
cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID);
cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID);
cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID);
// For curve set only
cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe);
@ -476,12 +843,12 @@ struct _cmsPipeline_struct {
_cmsOPTeval16Fn Eval16Fn;
_cmsPipelineEvalFloatFn EvalFloatFn;
_cmsOPTfreeDataFn FreeDataFn;
_cmsOPTdupDataFn DupDataFn;
_cmsFreeUserDataFn FreeDataFn;
_cmsDupUserDataFn DupDataFn;
cmsContext ContextID; // Environment
cmsBool SaveAs8Bits; // Implemntation-specific: save as 8 bits if possible
cmsBool SaveAs8Bits; // Implementation-specific: save as 8 bits if possible
};
// LUT reading & creation -------------------------------------------------------------------------------------------
@ -524,7 +891,8 @@ cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
cmsUInt16Number **Black,
cmsUInt32Number *nOutputs);
cmsBool _cmsOptimizePipeline(cmsPipeline** Lut,
cmsBool _cmsOptimizePipeline(cmsContext ContextID,
cmsPipeline** Lut,
int Intent,
cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat,
@ -549,26 +917,33 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type);
cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type);
cmsFormatter _cmsGetFormatter(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8
cmsFormatter _cmsGetFormatter(cmsContext ContextID,
cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8
cmsFormatterDirection Dir,
cmsUInt32Number dwFlags);
#ifndef CMS_NO_HALF_SUPPORT
// Half float
cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h);
cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt);
#endif
// 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
// 1-pixel cache (16 bits only)
cmsUInt16Number CacheIn[cmsMAXCHANNELS];
cmsUInt16Number CacheOut[cmsMAXCHANNELS];
} _cmsCACHE;
} cmsFormatterInfo;
// Transformation
typedef struct _cmstransform_struct {
@ -585,17 +960,13 @@ typedef struct _cmstransform_struct {
cmsFormatterFloat FromInputFloat;
cmsFormatterFloat ToOutputFloat;
// 1-pixel cache (16 bits only)
cmsUInt16Number CacheIn[cmsMAXCHANNELS];
cmsUInt16Number CacheOut[cmsMAXCHANNELS];
// 1-pixel cache seed for zero as input (16 bits, read only)
_cmsCACHE Cache;
// Semaphor for cache
LCMS_RWLOCK_T rwlock;
// A MPE LUT holding the full (optimized) transform
// A Pipeline holding the full (optimized) transform
cmsPipeline* Lut;
// A MPE LUT holding the gamut check. It goes from the input space to bilevel
// A Pipeline holding the gamut check. It goes from the input space to bilevel
cmsPipeline* GamutCheck;
// Colorant tables
@ -606,6 +977,10 @@ typedef struct _cmstransform_struct {
cmsColorSpaceSignature EntryColorSpace;
cmsColorSpaceSignature ExitColorSpace;
// White points (informative only)
cmsCIEXYZ EntryWhitePoint;
cmsCIEXYZ ExitWhitePoint;
// Profiles used to create the transform
cmsSEQ* Sequence;
@ -618,6 +993,10 @@ typedef struct _cmstransform_struct {
// An id that uniquely identifies the running context. May be null.
cmsContext ContextID;
// A user-defined pointer that can be used to store data for transform plug-ins
void* UserData;
_cmsFreeUserDataFn FreeUserData;
} _cmsTRANSFORM;
// --------------------------------------------------------------------------------------------------