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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -23,7 +23,7 @@
// //
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Version 2.1 // Version 2.6
// //
#ifndef _lcms2_H #ifndef _lcms2_H
@ -40,9 +40,6 @@
// Uncomment this if your compiler doesn't work with fast floor function // Uncomment this if your compiler doesn't work with fast floor function
// #define CMS_DONT_USE_FAST_FLOOR 1 // #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, // 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. // if commented, lcms will compute the black point by its own.
// It is safer to leave it commented out // It is safer to leave it commented out
@ -55,6 +52,12 @@
// require "KEYWORD" on undefined identifiers, keep it comented out unless needed // require "KEYWORD" on undefined identifiers, keep it comented out unless needed
// #define CMS_STRICT_CGATS 1 // #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 ****************************** // ********** End of configuration toggles ******************************
// Needed for streams // Needed for streams
@ -72,7 +75,7 @@ extern "C" {
#endif #endif
// Version/release // 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 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@ -81,6 +84,10 @@ extern "C" {
typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec
typedef signed char cmsInt8Number; // 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 // IEEE float storage numbers
typedef float cmsFloat32Number; typedef float cmsFloat32Number;
typedef double cmsFloat64Number; 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. // 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 // 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) #if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(WORDS_BIGENDIAN)
# define CMS_USE_BIG_ENDIAN 1 # define CMS_USE_BIG_ENDIAN 1
#endif #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 macintosh
# ifdef __BIG_ENDIAN__ # ifdef __BIG_ENDIAN__
# define CMS_USE_BIG_ENDIAN 1 # define CMS_USE_BIG_ENDIAN 1
# endif # endif
# ifdef __LITTLE_ENDIAN__
# undef CMS_USE_BIG_ENDIAN
# endif
#endif #endif
// Calling convention -- this is hardly platform and compiler dependent // Calling convention -- this is hardly platform and compiler dependent
@ -214,6 +237,14 @@ typedef int cmsBool;
# define CMSAPI # define CMSAPI
#endif #endif
#ifdef HasTHREADS
# if HasTHREADS == 1
# undef CMS_NO_PTHREADS
# else
# define CMS_NO_PTHREADS 1
# endif
#endif
// Some common definitions // Some common definitions
#define cmsMAX_PATH 256 #define cmsMAX_PATH 256
@ -247,6 +278,7 @@ typedef enum {
cmsSigCrdInfoType = 0x63726469, // 'crdi' cmsSigCrdInfoType = 0x63726469, // 'crdi'
cmsSigCurveType = 0x63757276, // 'curv' cmsSigCurveType = 0x63757276, // 'curv'
cmsSigDataType = 0x64617461, // 'data' cmsSigDataType = 0x64617461, // 'data'
cmsSigDictType = 0x64696374, // 'dict'
cmsSigDateTimeType = 0x6474696D, // 'dtim' cmsSigDateTimeType = 0x6474696D, // 'dtim'
cmsSigDeviceSettingsType = 0x64657673, // 'devs' cmsSigDeviceSettingsType = 0x64657673, // 'devs'
cmsSigLut16Type = 0x6d667432, // 'mft2' cmsSigLut16Type = 0x6d667432, // 'mft2'
@ -273,9 +305,10 @@ typedef enum {
cmsSigUInt32ArrayType = 0x75693332, // 'ui32' cmsSigUInt32ArrayType = 0x75693332, // 'ui32'
cmsSigUInt64ArrayType = 0x75693634, // 'ui64' cmsSigUInt64ArrayType = 0x75693634, // 'ui64'
cmsSigUInt8ArrayType = 0x75693038, // 'ui08' cmsSigUInt8ArrayType = 0x75693038, // 'ui08'
cmsSigVcgtType = 0x76636774, // 'vcgt'
cmsSigViewingConditionsType = 0x76696577, // 'view' cmsSigViewingConditionsType = 0x76696577, // 'view'
cmsSigXYZType = 0x58595A20, // 'XYZ ' cmsSigXYZType = 0x58595A20 // 'XYZ '
cmsSigVcgtType = 0x76636774 // 'vcgt'
} cmsTagTypeSignature; } cmsTagTypeSignature;
@ -330,6 +363,7 @@ typedef enum {
cmsSigPreview1Tag = 0x70726531, // 'pre1' cmsSigPreview1Tag = 0x70726531, // 'pre1'
cmsSigPreview2Tag = 0x70726532, // 'pre2' cmsSigPreview2Tag = 0x70726532, // 'pre2'
cmsSigProfileDescriptionTag = 0x64657363, // 'desc' cmsSigProfileDescriptionTag = 0x64657363, // 'desc'
cmsSigProfileDescriptionMLTag = 0x6473636d, // 'dscm'
cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq' cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq'
cmsSigProfileSequenceIdTag = 0x70736964, // 'psid' cmsSigProfileSequenceIdTag = 0x70736964, // 'psid'
cmsSigPs2CRD0Tag = 0x70736430, // 'psd0' cmsSigPs2CRD0Tag = 0x70736430, // 'psd0'
@ -348,7 +382,8 @@ typedef enum {
cmsSigUcrBgTag = 0x62666420, // 'bfd ' cmsSigUcrBgTag = 0x62666420, // 'bfd '
cmsSigViewingCondDescTag = 0x76756564, // 'vued' cmsSigViewingCondDescTag = 0x76756564, // 'vued'
cmsSigViewingConditionsTag = 0x76696577, // 'view' cmsSigViewingConditionsTag = 0x76696577, // 'view'
cmsSigVcgtTag = 0x76636774 // 'vcgt' cmsSigVcgtTag = 0x76636774, // 'vcgt'
cmsSigMetaTag = 0x6D657461 // 'meta'
} cmsTagSignature; } cmsTagSignature;
@ -407,12 +442,12 @@ typedef enum {
cmsSigMCH7Data = 0x4D434837, // 'MCH7' cmsSigMCH7Data = 0x4D434837, // 'MCH7'
cmsSigMCH8Data = 0x4D434838, // 'MCH8' cmsSigMCH8Data = 0x4D434838, // 'MCH8'
cmsSigMCH9Data = 0x4D434839, // 'MCH9' cmsSigMCH9Data = 0x4D434839, // 'MCH9'
cmsSigMCHAData = 0x4D43483A, // 'MCHA' cmsSigMCHAData = 0x4D434841, // 'MCHA'
cmsSigMCHBData = 0x4D43483B, // 'MCHB' cmsSigMCHBData = 0x4D434842, // 'MCHB'
cmsSigMCHCData = 0x4D43483C, // 'MCHC' cmsSigMCHCData = 0x4D434843, // 'MCHC'
cmsSigMCHDData = 0x4D43483D, // 'MCHD' cmsSigMCHDData = 0x4D434844, // 'MCHD'
cmsSigMCHEData = 0x4D43483E, // 'MCHE' cmsSigMCHEData = 0x4D434845, // 'MCHE'
cmsSigMCHFData = 0x4D43483F, // 'MCHF' cmsSigMCHFData = 0x4D434846, // 'MCHF'
cmsSigNamedData = 0x6e6d636c, // 'nmcl' cmsSigNamedData = 0x6e6d636c, // 'nmcl'
cmsSig1colorData = 0x31434C52, // '1CLR' cmsSig1colorData = 0x31434C52, // '1CLR'
cmsSig2colorData = 0x32434C52, // '2CLR' cmsSig2colorData = 0x32434C52, // '2CLR'
@ -483,7 +518,13 @@ typedef enum {
cmsSigLabV4toV2 = 0x34203220, // '4 2 ' cmsSigLabV4toV2 = 0x34203220, // '4 2 '
// Identities // Identities
cmsSigIdentityElemType = 0x69646E20 // 'idn ' cmsSigIdentityElemType = 0x69646E20, // 'idn '
// Float to floatPCS
cmsSigLab2FloatPCS = 0x64326C20, // 'd2l '
cmsSigFloatPCS2Lab = 0x6C326420, // 'l2d '
cmsSigXYZ2FloatPCS = 0x64327820, // 'd2x '
cmsSigFloatPCS2XYZ = 0x78326420 // 'x2d '
} cmsStageSignature; } cmsStageSignature;
@ -597,7 +638,6 @@ typedef struct {
// Little CMS specific typedefs // Little CMS specific typedefs
typedef void* cmsContext; // Context identifier for multithreaded environments
typedef void* cmsHANDLE ; // Generic handle typedef void* cmsHANDLE ; // Generic handle
typedef void* cmsHPROFILE; // Opaque typedefs to hide internals typedef void* cmsHPROFILE; // Opaque typedefs to hide internals
typedef void* cmsHTRANSFORM; typedef void* cmsHTRANSFORM;
@ -606,7 +646,9 @@ typedef void* cmsHTRANSFORM;
// Format of pixel is defined by one cmsUInt32Number, using bit fields as follows // 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 // 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 // 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_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 (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_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 (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 (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_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_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 (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 (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 (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)) #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_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_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_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)|DOSWAP_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_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_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)) #define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2))
@ -844,12 +889,17 @@ typedef void* cmsHTRANSFORM;
// Float formatters. // Float formatters.
#define TYPE_XYZ_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(4)) #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_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_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_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_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_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)) #define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4))
// Floating point formatters. // 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_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_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_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)) #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 #endif
// Colorspaces // Colorspaces
@ -944,10 +1007,25 @@ typedef struct {
CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2); CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2);
CMSAPI long int CMSEXPORT cmsfilelength(FILE* f); 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 cmsPlugin(void* Plugin);
CMSAPI cmsBool CMSEXPORT cmsPluginTHR(cmsContext ContextID, void* Plugin);
CMSAPI void CMSEXPORT cmsUnregisterPlugins(void); CMSAPI void CMSEXPORT cmsUnregisterPlugins(void);
CMSAPI void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID);
// Error logging ---------------------------------------------------------------------------------------------------------- // Error logging ----------------------------------------------------------------------------------------------------------
@ -984,6 +1062,7 @@ typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Numb
// Allows user to set any specific logger // Allows user to set any specific logger
CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn); CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn);
CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn);
// Conversions -------------------------------------------------------------------------------------------------------------- // Conversions --------------------------------------------------------------------------------------------------------------
@ -1090,6 +1169,10 @@ CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve*
CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t); CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t);
CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision); 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 ------------------------------------------------------------- // Implements pipelines of multi-processing elements -------------------------------------------------------------
@ -1102,6 +1185,7 @@ CMSAPI cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUIn
CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut); CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut);
CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig); CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig);
CMSAPI cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut);
CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut); CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut);
CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(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 // Where to place/locate the stages in the pipeline chain
typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc; 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); 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 // 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 #define SAMPLER_INSPECT 0x01000000
// For CLUT only // 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); CMSAPI cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags);
// Slicers // Slicers
CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
cmsSAMPLER16 Sampler, void * Cargo); cmsSAMPLER16 Sampler, void * Cargo);
@ -1203,6 +1286,13 @@ CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3], const char LanguageCode[3], const char CountryCode[3],
char ObtainedLanguage[3], char ObtainedCountry[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 ------------------------------------------------------------------------------------- // Undercolorremoval & black generation -------------------------------------------------------------------------------------
typedef struct { typedef struct {
@ -1275,6 +1365,7 @@ CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform);
// Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others // Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others
// come from Profile Sequence Identifier Tag // come from Profile Sequence Identifier Tag
typedef struct { typedef struct {
cmsSignature deviceMfg; cmsSignature deviceMfg;
cmsSignature deviceModel; cmsSignature deviceModel;
cmsUInt64Number attributes; cmsUInt64Number attributes;
@ -1298,6 +1389,27 @@ CMSAPI cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext
CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq); CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq);
CMSAPI void CMSEXPORT cmsFreeProfileSequenceDescription(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 ---------------------------------------------------------------------------------------------- // Access to Profile data ----------------------------------------------------------------------------------------------
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID); 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 void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile); CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile);
CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer); CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile); CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile);
CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model); CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model);
CMSAPI void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags); 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 cmsOpenProfileFromMem(const void * MemPtr, cmsUInt32Number dwSize);
CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, 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 cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io);
CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write);
CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile); CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile);
CMSAPI cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName); 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 // Call with NULL as parameters to get the intent count
CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions);
// Flags // Flags
@ -1605,15 +1720,37 @@ CMSAPI void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
void * OutputBuffer, void * OutputBuffer,
cmsUInt32Number Size); 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 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 // Adaptation state for absolute colorimetric intent
CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d); 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 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
CMSAPI cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform); 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 // For backwards compatibility
CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
cmsUInt32Number InputFormat, 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 cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* cProp, const char *Str);
CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val); 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 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 cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer);
CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp); CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp);
CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(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 cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames);
CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames);
// Datasets // Datasets
CMSAPI const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col); 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 int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames);
CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer); CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer);
CMSAPI int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch);
// The LABEL extension // The LABEL extension
CMSAPI int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType); 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 // Formatter for double
CMSAPI void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter); 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 // Estimate the black point
CMSAPI cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); 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 // Estimate total area coverage
CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile);

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -131,7 +131,7 @@ struct _cms_io_handler {
// Endianess adjust functions // Endianess adjust functions
CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word); CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word);
CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value); 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 // Helper IO functions
CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n); 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 _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n); CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number 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 _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n);
CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ); CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ);
CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array); 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 _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source);
CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest); 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 cmsPluginRenderingIntentSig 0x696E7448 // 'intH'
#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH' #define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH'
#define cmsPluginOptimizationSig 0x6F707448 // 'optH' #define cmsPluginOptimizationSig 0x6F707448 // 'optH'
#define cmsPluginTransformSig 0x7A666D48 // 'xfmH'
#define cmsPluginMutexSig 0x6D747A48 // 'mtxH'
typedef struct _cmsPluginBaseStruct { typedef struct _cmsPluginBaseStruct {
@ -212,19 +219,28 @@ typedef struct _cmsPluginBaseStruct {
//---------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------
// Memory handler. Each new plug-in type replaces current behaviour // 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 { typedef struct {
cmsPluginBase base; cmsPluginBase base;
// Required // Required
void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size); _cmsMallocFnPtrType MallocPtr;
void (* FreePtr)(cmsContext ContextID, void *Ptr); _cmsFreeFnPtrType FreePtr;
void * (* ReallocPtr)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); _cmsReallocFnPtrType ReallocPtr;
// Optional // Optional
void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size); _cmsMalloZerocFnPtrType MallocZeroPtr;
void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); _cmsCallocFnPtrType CallocPtr;
void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size); _cmsDupFnPtrType DupPtr;
} cmsPluginMemHandler; } cmsPluginMemHandler;
@ -387,7 +403,7 @@ typedef struct _cms_typehandler_struct {
void *Ptr); void *Ptr);
// Additional parameters used by the calling thread // Additional parameters used by the calling thread
cmsContext ContextID; cmsContext ContextID;
cmsUInt32Number ICCVersion; cmsUInt32Number ICCVersion;
} cmsTagTypeHandler; } cmsTagTypeHandler;
@ -486,6 +502,39 @@ typedef struct {
} cmsPluginMultiProcessElement; } 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. // Optimization. Using this plug-in, additional optimization strategies may be implemented.
// The function should return TRUE if any optimization is done on the LUT, this terminates // The 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 cmsUInt16Number Out[],
register const void* Data); register const void* Data);
typedef void (* _cmsOPTfreeDataFn)(cmsContext ContextID, void* Data);
typedef void* (* _cmsOPTdupDataFn)(cmsContext ContextID, const void* Data);
typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut, typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut,
cmsUInt32Number Intent, cmsUInt32Number Intent,
@ -512,8 +558,8 @@ typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut,
CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
_cmsOPTeval16Fn Eval16, _cmsOPTeval16Fn Eval16,
void* PrivateData, void* PrivateData,
_cmsOPTfreeDataFn FreePrivateDataFn, _cmsFreeUserDataFn FreePrivateDataFn,
_cmsOPTdupDataFn DupPrivateDataFn); _cmsDupUserDataFn DupPrivateDataFn);
typedef struct { typedef struct {
cmsPluginBase base; cmsPluginBase base;
@ -524,6 +570,62 @@ typedef struct {
} cmsPluginOptimization; } 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 #ifndef CMS_USE_CPP_API
# ifdef __cplusplus # ifdef __cplusplus

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -52,7 +52,7 @@ typedef struct {
cmsUInt32Number surround; cmsUInt32Number surround;
cmsFloat64Number n, Nbb, Ncb, z, FL, D; cmsFloat64Number n, Nbb, Ncb, z, FL, D;
cmsContext ContextID; cmsContext ContextID;
} cmsCIECAM02; } cmsCIECAM02;
@ -358,70 +358,70 @@ CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) 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) { if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) {
return NULL; return NULL;
} }
lpMod ->ContextID = ContextID; lpMod ->ContextID = ContextID;
lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
lpMod -> LA = pVC ->La; lpMod -> LA = pVC ->La;
lpMod -> Yb = pVC ->Yb; lpMod -> Yb = pVC ->Yb;
lpMod -> D = pVC ->D_value; lpMod -> D = pVC ->D_value;
lpMod -> surround = pVC ->surround; lpMod -> surround = pVC ->surround;
switch (lpMod -> surround) { switch (lpMod -> surround) {
case CUTSHEET_SURROUND: case CUTSHEET_SURROUND:
lpMod->F = 0.8; lpMod->F = 0.8;
lpMod->c = 0.41; lpMod->c = 0.41;
lpMod->Nc = 0.8; lpMod->Nc = 0.8;
break; break;
case DARK_SURROUND: case DARK_SURROUND:
lpMod -> F = 0.8; lpMod -> F = 0.8;
lpMod -> c = 0.525; lpMod -> c = 0.525;
lpMod -> Nc = 0.8; lpMod -> Nc = 0.8;
break; break;
case DIM_SURROUND: case DIM_SURROUND:
lpMod -> F = 0.9; lpMod -> F = 0.9;
lpMod -> c = 0.59; lpMod -> c = 0.59;
lpMod -> Nc = 0.95; lpMod -> Nc = 0.95;
break; break;
default: default:
// Average surround // Average surround
lpMod -> F = 1.0; lpMod -> F = 1.0;
lpMod -> c = 0.69; lpMod -> c = 0.69;
lpMod -> Nc = 1.0; lpMod -> Nc = 1.0;
} }
lpMod -> n = compute_n(lpMod); lpMod -> n = compute_n(lpMod);
lpMod -> z = compute_z(lpMod); lpMod -> z = compute_z(lpMod);
lpMod -> Nbb = computeNbb(lpMod); lpMod -> Nbb = computeNbb(lpMod);
lpMod -> FL = computeFL(lpMod); lpMod -> FL = computeFL(lpMod);
if (lpMod -> D == D_CALCULATE) { if (lpMod -> D == D_CALCULATE) {
lpMod -> D = computeD(lpMod); lpMod -> D = computeD(lpMod);
} }
lpMod -> Ncb = lpMod -> Nbb; lpMod -> Ncb = lpMod -> Nbb;
lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); 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; 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; CAM02COLOR clr;
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
_cmsAssert(lpMod != NULL); _cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL); _cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL); _cmsAssert(pOut != NULL);
memset(&clr, 0, sizeof(clr));
clr.XYZ[0] = pIn ->X; clr.XYZ[0] = pIn ->X;
clr.XYZ[1] = pIn ->Y; clr.XYZ[1] = pIn ->Y;
@ -462,9 +464,11 @@ void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ
CAM02COLOR clr; CAM02COLOR clr;
cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel;
_cmsAssert(lpMod != NULL); _cmsAssert(lpMod != NULL);
_cmsAssert(pIn != NULL); _cmsAssert(pIn != NULL);
_cmsAssert(pOut != NULL); _cmsAssert(pOut != NULL);
memset(&clr, 0, sizeof(clr));
clr.J = pIn -> J; clr.J = pIn -> J;
clr.C = pIn -> C; clr.C = pIn -> C;
@ -480,4 +484,3 @@ void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ
pOut ->Y = clr.XYZ[1]; pOut ->Y = clr.XYZ[1];
pOut ->Z = clr.XYZ[2]; pOut ->Z = clr.XYZ[2];
} }

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -30,8 +30,8 @@
// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- // IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
#define MAXID 128 // Max lenght of identifier #define MAXID 128 // Max length of identifier
#define MAXSTR 1024 // Max lenght of string #define MAXSTR 1024 // Max length of string
#define MAXTABLES 255 // Max Number of tables in a single stream #define MAXTABLES 255 // Max Number of tables in a single stream
#define MAXINCLUDE 20 // Max number of nested includes #define MAXINCLUDE 20 // Max number of nested includes
@ -44,6 +44,7 @@
# define DIR_CHAR '/' # define DIR_CHAR '/'
#endif #endif
// Symbols // Symbols
typedef enum { typedef enum {
@ -114,6 +115,8 @@ typedef struct _SubAllocator {
// Table. Each individual table can hold properties and rows & cols // Table. Each individual table can hold properties and rows & cols
typedef struct _Table { typedef struct _Table {
char SheetType[MAXSTR]; // The first row of the IT8 (the type)
int nSamples, nPatches; // Cols, Rows int nSamples, nPatches; // Cols, Rows
int SampleID; // Pos of ID int SampleID; // Pos of ID
@ -133,7 +136,6 @@ typedef struct _FileContext {
// This struct hold all information about an open IT8 handler. // This struct hold all information about an open IT8 handler.
typedef struct { typedef struct {
char SheetType[MAXSTR]; // The first row of the IT8 (the type)
cmsUInt32Number TablesCount; // How many tables in this stream cmsUInt32Number TablesCount; // How many tables in this stream
cmsUInt32Number nTable; // The actual table cmsUInt32Number nTable; // The actual table
@ -352,28 +354,28 @@ static const char* PredefinedSampleID[] = {
//Forward declaration of some internal functions //Forward declaration of some internal functions
static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
// Checks if c is a separator // Checks whatever c is a separator
static static
cmsBool isseparator(int c) 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 static
cmsBool ismiddle(int c) cmsBool ismiddle(int c)
{ {
return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127)); 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 static
cmsBool isidchar(int c) cmsBool isidchar(int c)
{ {
return isalnum(c) || ismiddle(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 static
cmsBool isfirstidchar(int c) cmsBool isfirstidchar(int c)
{ {
@ -404,6 +406,7 @@ cmsBool isabsolutepath(const char *path)
return FALSE; return FALSE;
} }
// Makes a file path based on a given reference path // 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 // NOTE: this function doesn't check if the path exists or even if it's legal
static static
@ -545,7 +548,7 @@ void ReadReal(cmsIT8* it8, int inum)
if (it8->ch == '.') { // Decimal point if (it8->ch == '.') { // Decimal point
cmsFloat64Number frac = 0.0; // fraction cmsFloat64Number frac = 0.0; // fraction
int prec = 0; // precision int prec = 0; // precision
NextCh(it8); // Eats dec. point 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 // Reads next symbol
@ -759,6 +840,14 @@ void InSymbol(cmsIT8* it8)
// Next line // Next line
case '\r':
NextCh(it8);
if (it8 ->ch == '\n')
NextCh(it8);
it8->sy = SEOLN;
it8->lineno++;
break;
case '\n': case '\n':
NextCh(it8); NextCh(it8);
it8->sy = SEOLN; it8->sy = SEOLN;
@ -768,7 +857,7 @@ void InSymbol(cmsIT8* it8)
// Comment // Comment
case '#': case '#':
NextCh(it8); NextCh(it8);
while (it8->ch && it8->ch != '\n') while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
NextCh(it8); NextCh(it8);
it8->sy = SCOMMENT; it8->sy = SCOMMENT;
@ -886,6 +975,9 @@ cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* Error
{ {
switch (it8->sy) { switch (it8->sy) {
case SEOLN: // Empty value
Buffer[0]=0;
break;
case SIDENT: strncpy(Buffer, it8->id, max); case SIDENT: strncpy(Buffer, it8->id, max);
Buffer[max-1]=0; Buffer[max-1]=0;
break; break;
@ -982,7 +1074,7 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
cmsUInt8Number* ptr; cmsUInt8Number* ptr;
size = _cmsALIGNLONG(size); size = _cmsALIGNMEM(size);
if (size > Free) { if (size > Free) {
@ -1035,9 +1127,9 @@ cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYV
if (*Key != '#') { // Comments are ignored if (*Key != '#') { // Comments are ignored
if (cmsstrcasecmp(Key, p->Keyword) == 0) if (cmsstrcasecmp(Key, p->Keyword) == 0)
break; break;
}
} }
}
if (p == NULL) if (p == NULL)
return FALSE; return FALSE;
@ -1047,11 +1139,13 @@ cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYV
for (; p != NULL; p = p->NextSubkey) { for (; p != NULL; p = p->NextSubkey) {
if (p ->Subkey == NULL) continue;
if (LastPtr) *LastPtr = p; if (LastPtr) *LastPtr = p;
if (cmsstrcasecmp(Subkey, p->Subkey) == 0) if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
} }
@ -1174,7 +1268,7 @@ cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
it8 ->nTable = 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) cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
{ {
cmsIT8* it8; cmsIT8* it8;
int i; cmsUInt32Number i;
it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8)); it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
if (it8 == NULL) return NULL; if (it8 == NULL) return NULL;
@ -1214,7 +1308,7 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
it8 -> lineno = 1; it8 -> lineno = 1;
strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
strcpy(it8->SheetType, "CGATS.17"); cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
// Initialize predefined properties & data // Initialize predefined properties & data
@ -1231,18 +1325,15 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; return GetTable((cmsIT8*) hIT8)->SheetType;
return it8 ->SheetType;
} }
cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type) cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; TABLE* t = GetTable((cmsIT8*) hIT8);
strncpy(it8 ->SheetType, Type, MAXSTR-1); strncpy(t ->SheetType, Type, MAXSTR-1);
it8 ->SheetType[MAXSTR-1] = 0; t ->SheetType[MAXSTR-1] = 0;
return TRUE; 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; return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
} }
// Sets a property // Sets a property
cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val) 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; return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
} }
cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val) cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
@ -1285,7 +1373,7 @@ cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUIn
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
char Buffer[1024]; char Buffer[1024];
sprintf(Buffer, "%d", Val); sprintf(Buffer, "%u", Val);
return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL; 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); const char *v = cmsIT8GetProperty(hIT8, cProp);
if (v) return atof(v); if (v == NULL) return 0.0;
else return 0.0;
return ParseFloatNumber(v);
} }
const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey) const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
@ -1355,7 +1444,7 @@ void AllocateDataFormat(cmsIT8* it8)
t -> nSamples = 10; 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) { if (t->DataFormat == NULL) {
SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); 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-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); 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) { if (t->Data == NULL) {
SynError(it8, "AllocateDataSet: Unable to allocate data array"); SynError(it8, "AllocateDataSet: Unable to allocate data array");
@ -1470,7 +1559,7 @@ void WriteStr(SAVESTREAM* f, const char *str)
if (str == NULL) if (str == NULL)
str = " "; str = " ";
// Lenghth to write // Length to write
len = (cmsUInt32Number) strlen(str); len = (cmsUInt32Number) strlen(str);
f ->Used += len; f ->Used += len;
@ -1524,6 +1613,9 @@ void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
KEYVALUE* p; KEYVALUE* p;
TABLE* t = GetTable(it8); TABLE* t = GetTable(it8);
// Writes the type
WriteStr(fp, t->SheetType);
WriteStr(fp, "\n");
for (p = t->HeaderList; (p != NULL); p = p->Next) 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"); sd.stream = fopen(cFileName, "wt");
if (!sd.stream) return FALSE; if (!sd.stream) return FALSE;
WriteStr(&sd, it8->SheetType);
WriteStr(&sd, "\n");
for (i=0; i < it8 ->TablesCount; i++) { for (i=0; i < it8 ->TablesCount; i++) {
cmsIT8SetTable(hIT8, i); cmsIT8SetTable(hIT8, i);
@ -1708,20 +1798,18 @@ cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number*
else else
sd.Max = 0; // Just counting the needed bytes sd.Max = 0; // Just counting the needed bytes
WriteStr(&sd, it8->SheetType);
WriteStr(&sd, "\n");
for (i=0; i < it8 ->TablesCount; i++) { for (i=0; i < it8 ->TablesCount; i++) {
cmsIT8SetTable(hIT8, i); cmsIT8SetTable(hIT8, i);
WriteHeader(it8, &sd); WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8); WriteDataFormat(&sd, it8);
WriteData(&sd, it8); WriteData(&sd, it8);
} }
sd.Used++; // The \0 at the very end sd.Used++; // The \0 at the very end
if (sd.Base) if (sd.Base)
sd.Ptr = 0; *sd.Ptr = 0;
*BytesNeeded = sd.Used; *BytesNeeded = sd.Used;
@ -1934,12 +2022,8 @@ cmsBool HeaderSection(cmsIT8* it8)
static 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. // First line is a very special case.
while (isseparator(it8->ch)) while (isseparator(it8->ch))
@ -1950,9 +2034,20 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
*SheetTypePtr++= (char) it8 ->ch; *SheetTypePtr++= (char) it8 ->ch;
NextCh(it8); NextCh(it8);
} }
}
*SheetTypePtr = 0; *SheetTypePtr = 0;
}
static
cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
{
char* SheetTypePtr = it8 ->Tab[0].SheetType;
if (nosheet == 0) {
ReadType(it8, SheetTypePtr);
}
InSymbol(it8); InSymbol(it8);
SkipEOLN(it8); SkipEOLN(it8);
@ -1974,6 +2069,39 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
AllocTable(it8); AllocTable(it8);
it8 ->nTable = it8 ->TablesCount - 1; 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; break;
@ -2022,9 +2150,9 @@ void CookPointers(cmsIT8* it8)
if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { 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); char *Data = GetData(it8, i, idField);
if (Data) { if (Data) {
@ -2039,7 +2167,7 @@ void CookPointers(cmsIT8* it8)
SetData(it8, i, idField, Buffer); SetData(it8, i, idField, Buffer);
} }
} }
} }
@ -2070,7 +2198,7 @@ void CookPointers(cmsIT8* it8)
char Buffer[256]; char Buffer[256];
char *Type = p ->Value; char *Type = p ->Value;
int nTable = k; int nTable = (int) k;
snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type ); 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 // 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 // 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 static
int IsMyBlock(cmsUInt8Number* Buffer, int n) int IsMyBlock(cmsUInt8Number* Buffer, int n)
{ {
int cols = 1, space = 0, quot = 0; int words = 1, space = 0, quot = 0;
int i; int i;
if (n < 10) return FALSE; // Too small if (n < 10) return 0; // Too small
if (n > 132) if (n > 132)
n = 132; n = 132;
@ -2112,7 +2240,7 @@ int IsMyBlock(cmsUInt8Number* Buffer, int n)
{ {
case '\n': case '\n':
case '\r': case '\r':
return quot == 1 || cols > 2 ? 0 : cols; return ((quot == 1) || (words > 2)) ? 0 : words;
case '\t': case '\t':
case ' ': case ' ':
if(!quot && !space) if(!quot && !space)
@ -2124,14 +2252,13 @@ int IsMyBlock(cmsUInt8Number* Buffer, int n)
default: default:
if (Buffer[i] < 32) return 0; if (Buffer[i] < 32) return 0;
if (Buffer[i] > 127) return 0; if (Buffer[i] > 127) return 0;
cols += space; words += space;
space = 0; space = 0;
break; break;
} }
} }
return FALSE; return 0;
} }
@ -2151,7 +2278,7 @@ cmsBool IsMyFile(const char* FileName)
Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp); Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
if (fclose(fp) != 0) if (fclose(fp) != 0)
return FALSE; return FALSE;
Ptr[Size] = '\0'; Ptr[Size] = '\0';
@ -2165,10 +2292,10 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt3
{ {
cmsHANDLE hIT8; cmsHANDLE hIT8;
cmsIT8* it8; cmsIT8* it8;
int type; int type;
_cmsAssert(Ptr != NULL); _cmsAssert(Ptr != NULL);
_cmsAssert(len != 0); _cmsAssert(len != 0);
type = IsMyBlock((cmsUInt8Number*)Ptr, len); type = IsMyBlock((cmsUInt8Number*)Ptr, len);
if (type == 0) return NULL; if (type == 0) return NULL;
@ -2208,11 +2335,11 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileN
cmsHANDLE hIT8; cmsHANDLE hIT8;
cmsIT8* it8; cmsIT8* it8;
int type; int type;
_cmsAssert(cFileName != NULL); _cmsAssert(cFileName != NULL);
type = IsMyFile(cFileName); type = IsMyFile(cFileName);
if (type == 0) return NULL; if (type == 0) return NULL;
hIT8 = cmsIT8Alloc(ContextID); hIT8 = cmsIT8Alloc(ContextID);
@ -2241,10 +2368,10 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileN
CookPointers(it8); CookPointers(it8);
it8 ->nTable = 0; it8 ->nTable = 0;
if (fclose(it8 ->FileStack[0]->Stream)!= 0) { if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
cmsIT8Free(hIT8); cmsIT8Free(hIT8);
return NULL; return NULL;
} }
return hIT8; return hIT8;
@ -2252,16 +2379,16 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileN
int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames) int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t; TABLE* t;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
t = GetTable(it8); t = GetTable(it8);
if (SampleNames) if (SampleNames)
*SampleNames = t -> DataFormat; *SampleNames = t -> DataFormat;
return t -> nSamples; return t -> nSamples;
} }
@ -2273,9 +2400,9 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyN
char **Props; char **Props;
TABLE* t; TABLE* t;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
t = GetTable(it8); t = GetTable(it8);
// Pass#1 - count properties // Pass#1 - count properties
@ -2305,10 +2432,10 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cP
const char **Props; const char **Props;
TABLE* t; TABLE* t;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
t = GetTable(it8); t = GetTable(it8);
if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) { if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
*SubpropertyNames = 0; *SubpropertyNames = 0;
@ -2402,7 +2529,7 @@ int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
return LocateSample(it8, cSample); return LocateSample(it8, cSample);
} }
@ -2413,7 +2540,7 @@ const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
return GetData(it8, row, col); return GetData(it8, row, col);
} }
@ -2425,13 +2552,9 @@ cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int c
Buffer = cmsIT8GetDataRowCol(hIT8, row, col); Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
if (Buffer) { if (Buffer == NULL) return 0.0;
return atof(Buffer);
} else
return 0;
return ParseFloatNumber(Buffer);
} }
@ -2439,7 +2562,7 @@ cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const ch
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
return SetData(it8, row, col, Val); return SetData(it8, row, col, Val);
} }
@ -2450,7 +2573,7 @@ cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFl
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256]; char Buff[256];
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
sprintf(Buff, it8->DoubleFormatter, Val); sprintf(Buff, it8->DoubleFormatter, Val);
@ -2464,7 +2587,7 @@ const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const ch
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
int iField, iSet; int iField, iSet;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
iField = LocateSample(it8, cSample); iField = LocateSample(it8, cSample);
if (iField < 0) { if (iField < 0) {
@ -2486,14 +2609,7 @@ cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch,
Buffer = cmsIT8GetData(it8, cPatch, cSample); Buffer = cmsIT8GetData(it8, cPatch, cSample);
if (Buffer) { return ParseFloatNumber(Buffer);
return atof(Buffer);
} else {
return 0;
}
} }
@ -2504,9 +2620,9 @@ cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char*
int iField, iSet; int iField, iSet;
TABLE* t; TABLE* t;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
t = GetTable(it8); t = GetTable(it8);
iField = LocateSample(it8, cSample); 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, cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
const char* cSample, const char* cSample,
cmsFloat64Number Val) cmsFloat64Number Val)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256]; char Buff[256];
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
snprintf(Buff, 255, it8->DoubleFormatter, Val); snprintf(Buff, 255, it8->DoubleFormatter, Val);
return cmsIT8SetData(hIT8, cPatch, cSample, Buff); return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
} }
// Buffer should get MAXSTR at least // Buffer should get MAXSTR at least
const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer) const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t; TABLE* t;
char* Data; char* Data;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
t = GetTable(it8); t = GetTable(it8);
Data = GetData(it8, nPatch, t->SampleID); Data = GetData(it8, nPatch, t->SampleID);
if (!Data) return NULL; if (!Data) return NULL;
if (!buffer) return Data; if (!buffer) return Data;
strncpy(buffer, Data, MAXSTR-1); strncpy(buffer, Data, MAXSTR-1);
buffer[MAXSTR-1] = 0; buffer[MAXSTR-1] = 0;
return buffer; return buffer;
} }
int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
{ {
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
return LocatePatch((cmsIT8*)hIT8, cPatch); return LocatePatch((cmsIT8*)hIT8, cPatch);
} }
cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8) 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. // 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) cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
{ {
cmsIT8* it8 = (cmsIT8*) hIT8; cmsIT8* it8 = (cmsIT8*) hIT8;
int pos; int pos;
_cmsAssert(hIT8 != NULL); _cmsAssert(hIT8 != NULL);
pos = LocateSample(it8, cSample); pos = LocateSample(it8, cSample);
if(pos == -1) if(pos == -1)
return FALSE; return FALSE;
it8->Tab[it8->nTable].SampleID = pos; it8->Tab[it8->nTable].SampleID = pos;
return TRUE; return TRUE;
} }
@ -2650,6 +2766,8 @@ void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
if (Formatter == NULL) if (Formatter == NULL)
strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
else 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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 // 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 // Search the list for a suitable intent. Returns NULL if not found
static static
cmsIntentsList* SearchIntent(cmsUInt32Number Intent) cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent)
{ {
_cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin);
cmsIntentsList* pt; 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; if (pt ->Intent == Intent) return pt;
return NULL; return NULL;
@ -164,17 +217,21 @@ void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn,
static static
cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
{ {
// Convert D50 across CHAD to get the absolute white point // Convert D50 across inverse CHAD to get the absolute white point
cmsVEC3 d, s; cmsVEC3 d, s;
cmsCIEXYZ Dest; cmsCIEXYZ Dest;
cmsCIExyY DestChromaticity; cmsCIExyY DestChromaticity;
cmsFloat64Number TempK; cmsFloat64Number TempK;
cmsMAT3 m1, m2;
m1 = *Chad;
if (!_cmsMAT3inverse(&m1, &m2)) return FALSE;
s.n[VX] = cmsD50_XYZ() -> X; s.n[VX] = cmsD50_XYZ() -> X;
s.n[VY] = cmsD50_XYZ() -> Y; s.n[VY] = cmsD50_XYZ() -> Y;
s.n[VZ] = cmsD50_XYZ() -> Z; s.n[VZ] = cmsD50_XYZ() -> Z;
_cmsMAT3eval(&d, Chad, &s); _cmsMAT3eval(&d, &m2, &s);
Dest.X = d.n[VX]; Dest.X = d.n[VX];
Dest.Y = d.n[VY]; Dest.Y = d.n[VY];
@ -190,15 +247,14 @@ cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
// Compute a CHAD based on a given temperature // Compute a CHAD based on a given temperature
static static
void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
{ {
cmsCIEXYZ White; cmsCIEXYZ White;
cmsCIExyY ChromaticityOfWhite; cmsCIExyY ChromaticityOfWhite;
cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp);
cmsxyY2XYZ(&White, &ChromaticityOfWhite); 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. // Join scalings to obtain relative input to absolute and then to relative output.
@ -211,7 +267,7 @@ cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
const cmsMAT3* ChromaticAdaptationMatrixOut, const cmsMAT3* ChromaticAdaptationMatrixOut,
cmsMAT3* m) cmsMAT3* m)
{ {
cmsMAT3 Scale, m1, m2, m3; cmsMAT3 Scale, m1, m2, m3, m4;
// Adaptation state // Adaptation state
if (AdaptationState == 1.0) { 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[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0);
_cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); _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) { 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 // 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 { } else {
cmsMAT3 MixedCHAD; cmsMAT3 MixedCHAD;
cmsFloat64Number TempSrc, TempDest, Temp; cmsFloat64Number TempSrc, TempDest, Temp;
TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); // K for source white m1 = *ChromaticAdaptationMatrixIn;
TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); // K for dest white 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 if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong
@ -256,9 +321,9 @@ cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState,
return TRUE; 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); Temp2CHAD(&MixedCHAD, Temp);
_cmsMAT3per(m, &m3, &MixedCHAD); _cmsMAT3per(m, &m3, &MixedCHAD);
@ -333,7 +398,7 @@ cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[],
cmsCIEXYZ BlackPointIn, BlackPointOut; cmsCIEXYZ BlackPointIn, BlackPointOut;
cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); 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 black points are equal, then do nothing
if (BlackPointIn.X != BlackPointOut.X || 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 // Handle PCS mismatches. A specialized stage is added to the LUT in such case
switch (InPCS) { switch (InPCS) {
case cmsSigXYZData: // Input profile operates in XYZ case cmsSigXYZData: // Input profile operates in XYZ
switch (OutPCS) { switch (OutPCS) {
case cmsSigXYZData: // XYZ -> XYZ case cmsSigXYZData: // XYZ -> XYZ
if (!IsEmptyLayer(m, off)) if (!IsEmptyLayer(m, off) &&
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)); !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)))
break; return FALSE;
break;
case cmsSigLabData: // XYZ -> Lab case cmsSigLabData: // XYZ -> Lab
if (!IsEmptyLayer(m, off)) if (!IsEmptyLayer(m, off) &&
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)); !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; if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID)))
return FALSE;
break;
default: default:
return FALSE; // Colorspace mismatch return FALSE; // Colorspace mismatch
} }
break; 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)); case cmsSigLabData: // Lab -> Lab
if (!IsEmptyLayer(m, off))
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl));
break;
case cmsSigLabData: // Lab -> Lab if (!IsEmptyLayer(m, off)) {
if (!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)) ||
cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)); !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID)))
cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)); return FALSE;
cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID));
}
break;
default:
return FALSE; // Mismatch
} }
break; break;
// On colorspaces other than PCS, check for same space
default: default:
if (InPCS != OutPCS) return FALSE; return FALSE; // Mismatch
break; }
break;
// On colorspaces other than PCS, check for same space
default:
if (InPCS != OutPCS) return FALSE;
break;
} }
return TRUE; return TRUE;
@ -434,6 +503,10 @@ cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature
// If they are same, they are compatible. // If they are same, they are compatible.
if (a == b) return TRUE; 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. // 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 == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE;
if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE;
@ -452,7 +525,8 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
cmsFloat64Number AdaptationStates[], cmsFloat64Number AdaptationStates[],
cmsUInt32Number dwFlags) cmsUInt32Number dwFlags)
{ {
cmsPipeline* Lut, *Result; cmsPipeline* Lut = NULL;
cmsPipeline* Result;
cmsHPROFILE hProfile; cmsHPROFILE hProfile;
cmsMAT3 m; cmsMAT3 m;
cmsVEC3 off; cmsVEC3 off;
@ -460,8 +534,8 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
cmsProfileClassSignature ClassSig; cmsProfileClassSignature ClassSig;
cmsUInt32Number i, Intent; cmsUInt32Number i, Intent;
// For safety // For safety
if (nProfiles == 0) return NULL; if (nProfiles == 0) return NULL;
// Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined'
Result = cmsPipelineAlloc(ContextID, 0, 0); Result = cmsPipelineAlloc(ContextID, 0, 0);
@ -478,14 +552,14 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass );
// First profile is used as input unless devicelink or abstract // First profile is used as input unless devicelink or abstract
if ((i == 0) && !lIsDeviceLink) { if ((i == 0) && !lIsDeviceLink) {
lIsInput = TRUE; lIsInput = TRUE;
} }
else { else {
// Else use profile in the input direction if current space is not PCS // Else use profile in the input direction if current space is not PCS
lIsInput = (CurrentColorSpace != cmsSigXYZData) && lIsInput = (CurrentColorSpace != cmsSigXYZData) &&
(CurrentColorSpace != cmsSigLabData); (CurrentColorSpace != cmsSigLabData);
} }
Intent = TheIntents[i]; Intent = TheIntents[i];
@ -508,7 +582,7 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
// If devicelink is found, then no custom intent is allowed and we can // If devicelink is found, then no custom intent is allowed and we can
// read the LUT to be applied. Settings don't apply here. // 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 // Get the involved LUT from the profile
Lut = _cmsReadDevicelinkLUT(hProfile, Intent); Lut = _cmsReadDevicelinkLUT(hProfile, Intent);
@ -548,8 +622,11 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
} }
// Concatenate to the output LUT // Concatenate to the output LUT
cmsPipelineCat(Result, Lut); if (!cmsPipelineCat(Result, Lut))
goto Error;
cmsPipelineFree(Lut); cmsPipelineFree(Lut);
Lut = NULL;
// Update current space // Update current space
CurrentColorSpace = ColorSpaceOut; CurrentColorSpace = ColorSpaceOut;
@ -559,6 +636,7 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID,
Error: Error:
if (Lut != NULL) cmsPipelineFree(Lut);
if (Result != NULL) cmsPipelineFree(Result); if (Result != NULL) cmsPipelineFree(Result);
return NULL; return NULL;
@ -697,7 +775,8 @@ cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
if (CLUT == NULL) goto Error; if (CLUT == NULL) goto Error;
// This is the one and only MPE in this LUT // 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. // Sample it. We cannot afford pre/post linearization this time.
if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0))
@ -847,7 +926,8 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
// Check for non-cmyk profiles // Check for non-cmyk profiles
if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || 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); return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags);
// Allocate an empty LUT for holding the result // Allocate an empty LUT for holding the result
@ -864,6 +944,8 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
// Get total area coverage (in 0..1 domain) // Get total area coverage (in 0..1 domain)
bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0;
if (bp.MaxTAC <= 0) goto Cleanup;
// Create a LUT holding normal ICC transform // Create a LUT holding normal ICC transform
bp.cmyk2cmyk = DefaultICCintents(ContextID, bp.cmyk2cmyk = DefaultICCintents(ContextID,
@ -873,6 +955,7 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
BPC, BPC,
AdaptationStates, AdaptationStates,
dwFlags); dwFlags);
if (bp.cmyk2cmyk == NULL) goto Cleanup;
// Now the tone curve // Now the tone curve
bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles,
@ -881,7 +964,7 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
BPC, BPC,
AdaptationStates, AdaptationStates,
dwFlags); dwFlags);
if (bp.KTone == NULL) goto Cleanup;
// To measure the output, Last profile to Lab // To measure the output, Last profile to Lab
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
@ -889,13 +972,15 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL,
INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
if ( bp.hProofOutput == NULL) goto Cleanup;
// Same as anterior, but lab in the 0..1 range // Same as anterior, but lab in the 0..1 range
bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1],
FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, 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, INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);
if (bp.cmyk2Lab == NULL) goto Cleanup;
cmsCloseProfile(hLab); cmsCloseProfile(hLab);
// Error estimation (for debug only) // Error estimation (for debug only)
@ -908,7 +993,8 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL);
if (CLUT == NULL) goto Cleanup; if (CLUT == NULL) goto Cleanup;
cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT); if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT))
goto Cleanup;
cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); 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 // 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. // preserve primaries. This solution is not perfect, but works well on most cases.
Intent = SearchIntent(TheIntents[0]); Intent = SearchIntent(ContextID, TheIntents[0]);
if (Intent == NULL) { if (Intent == NULL) {
cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]);
return NULL; return NULL;
} }
// Call the handler // Call the handler
return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); 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" // 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 // 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. // 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; cmsIntentsList* pt;
cmsUInt32Number nIntents; 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 (nIntents < nMax) {
if (Codes != NULL) if (Codes != NULL)
@ -1002,37 +1090,52 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32
nIntents++; 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; return nIntents;
} }
// The plug-in registration. User can add new intents or override default routines cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions)
cmsBool _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Data)
{ {
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; cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data;
cmsIntentsList* fl; cmsIntentsList* fl;
// Do we have to reset the intents? // Do we have to reset the custom intents?
if (Data == NULL) { if (Data == NULL) {
Intents = DefaultIntents; ctx->Intents = NULL;
return TRUE; 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; fl ->Intent = Plugin ->Intent;
strncpy(fl ->Description, Plugin ->Description, 255); strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1);
fl ->Description[255] = 0; fl ->Description[sizeof(fl ->Description)-1] = 0;
fl ->Link = Plugin ->Link; fl ->Link = Plugin ->Link;
fl ->Next = Intents; fl ->Next = ctx ->Intents;
Intents = fl; ctx ->Intents = fl;
return TRUE; return TRUE;
} }

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -31,13 +31,14 @@
// compare two strings ignoring case // compare two strings ignoring case
int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2) int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2)
{ {
register const unsigned char *us1 = (const unsigned char *)s1, register const unsigned char *us1 = (const unsigned char *)s1,
*us2 = (const unsigned char *)s2; *us2 = (const unsigned char *)s2;
while (toupper(*us1) == toupper(*us2++)) while (toupper(*us1) == toupper(*us2++))
if (*us1++ == '\0') if (*us1++ == '\0')
return (0); return 0;
return (toupper(*us1) - toupper(*--us2));
return (toupper(*us1) - toupper(*--us2));
} }
// long int because C99 specifies ftell in such way (7.19.9.2) // 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 p = ftell(f); // register current file position
if (fseek(f, 0, SEEK_END) != 0) { if (fseek(f, 0, SEEK_END) != 0) {
return -1; return -1;
} }
n = ftell(f); n = ftell(f);
fseek(f, p, SEEK_SET); // file position restored 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 // 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 // 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 // amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent
// prevent bogus or malintentionated code to allocate huge blocks that otherwise lcms // bogus or evil code to allocate huge blocks that otherwise lcms would never need.
// would never need.
#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) #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 // required to be implemented: malloc, realloc and free, although the user may want to
// replace the optional mallocZero, calloc and dup as well. // 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); 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. // realloc behaves the same way as malloc and allocates a new block of size bytes.
static static
void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size) void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size)
@ -167,82 +167,132 @@ void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number si
return mem; return mem;
} }
// Pointers to malloc and _cmsFree functions in current environment
static void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocDefaultFn; // Pointers to memory manager functions in Context0
static void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocZeroDefaultFn; _cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn,
static void (* FreePtr)(cmsContext ContextID, void *Ptr) = _cmsFreeDefaultFn; _cmsReallocDefaultFn, _cmsCallocDefaultFn, _cmsDupDefaultFn
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;
// 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 // Plug-in replacement entry
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase *Data) cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data)
{ {
cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) 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) { if (Data == NULL) {
MallocPtr = _cmsMallocDefaultFn; struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID;
MallocZeroPtr= _cmsMallocZeroDefaultFn;
FreePtr = _cmsFreeDefaultFn; // Return to the default allocators
ReallocPtr = _cmsReallocDefaultFn; if (ContextID != NULL) {
CallocPtr = _cmsCallocDefaultFn; ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager;
DupPtr = _cmsDupDefaultFn; }
return TRUE; return TRUE;
} }
// Check for required callbacks // Check for required callbacks
if (Plugin -> MallocPtr == NULL || if (Plugin -> MallocPtr == NULL ||
Plugin -> FreePtr == NULL || Plugin -> FreePtr == NULL ||
Plugin -> ReallocPtr == NULL) return FALSE; Plugin -> ReallocPtr == NULL) return FALSE;
// Set replacement functions // Set replacement functions
MallocPtr = Plugin -> MallocPtr; ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
FreePtr = Plugin -> FreePtr; if (ptr == NULL)
ReallocPtr = Plugin -> ReallocPtr; return FALSE;
if (Plugin ->MallocZeroPtr != NULL) MallocZeroPtr = Plugin ->MallocZeroPtr;
if (Plugin ->CallocPtr != NULL) CallocPtr = Plugin -> CallocPtr;
if (Plugin ->DupPtr != NULL) DupPtr = Plugin -> DupPtr;
_cmsInstallAllocFunctions(Plugin, ptr);
return TRUE; return TRUE;
} }
// Generic allocate // Generic allocate
void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) 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 // Generic allocate & zero
void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size)
{ {
return MallocZeroPtr(ContextID, size); _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin);
return ptr->MallocZeroPtr(ContextID, size);
} }
// Generic calloc // Generic calloc
void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) 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 // Generic reallocate
void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) 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 // Generic free memory
void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) 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 // Generic block duplication
void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) 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 // 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 // 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 // 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 static
_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
{ {
_cmsSubAllocator_chunk* chunk; _cmsSubAllocator_chunk* chunk;
// 20K by default
if (Initial == 0)
Initial = 20*1024;
// Create the container // Create the container
chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk)); chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk));
if (chunk == NULL) return NULL; if (chunk == NULL) return NULL;
@ -269,10 +323,6 @@ _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32N
return NULL; return NULL;
} }
// 20K by default
if (Initial == 0)
Initial = 20*1024;
chunk ->BlockSize = Initial; chunk ->BlockSize = Initial;
chunk ->Used = 0; chunk ->Used = 0;
chunk ->next = NULL; chunk ->next = NULL;
@ -325,7 +375,7 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used; cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used;
cmsUInt8Number* ptr; cmsUInt8Number* ptr;
size = _cmsALIGNLONG(size); size = _cmsALIGNMEM(size);
// Check for memory. If there is no room, allocate a new chunk of double memory size. // Check for memory. If there is no room, allocate a new chunk of double memory size.
if (size > Free) { if (size > Free) {
@ -351,6 +401,26 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size)
return (void*) ptr; 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 ****************************************************************** // Error logging ******************************************************************
// There is no error handling at all. When a funtion fails, it returns proper value. // 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 // This is our default log error
static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text);
// The current handler in actual environment // Context0 storage, which is global
static cmsLogErrorHandlerFunction LogErrorHandler = DefaultLogErrorHandlerFunction; _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. // The default error logger does nothing.
static static
@ -384,16 +472,27 @@ void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorC
cmsUNUSED_PARAMETER(ContextID); cmsUNUSED_PARAMETER(ContextID);
cmsUNUSED_PARAMETER(ErrorCode); 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) void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn)
{ {
if (Fn == NULL) cmsSetLogErrorHandlerTHR(NULL, Fn);
LogErrorHandler = DefaultLogErrorHandlerFunction;
else
LogErrorHandler = Fn;
} }
// Log an error // Log an error
@ -402,13 +501,18 @@ void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, c
{ {
va_list args; va_list args;
char Buffer[MAX_ERROR_MESSAGE_LEN]; char Buffer[MAX_ERROR_MESSAGE_LEN];
_cmsLogErrorChunkType* lhg;
va_start(args, ErrorText); va_start(args, ErrorText);
vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args); vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args);
va_end(args); va_end(args);
// Call handler // Check for the context, if specified go there. If not, go for the global
LogErrorHandler(ContextID, ErrorCode, Buffer); lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger);
if (lhg ->LogErrorHandler) {
lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer);
}
} }
// Utility function to print signatures // Utility function to print signatures
@ -426,3 +530,125 @@ void _cmsTagSignature2String(char String[5], cmsTagSignature sig)
String[4] = 0; 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -53,7 +53,6 @@ typedef struct _cmsParametricCurvesCollection_st {
} _cmsParametricCurvesCollection; } _cmsParametricCurvesCollection;
// This is the default (built-in) evaluator // This is the default (built-in) evaluator
static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R);
@ -66,22 +65,77 @@ static _cmsParametricCurvesCollection DefaultCurves = {
NULL // Next in chain 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 // The linked list head
static _cmsParametricCurvesCollection* ParametricCurves = &DefaultCurves; _cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL };
// As a way to install new parametric curves // 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; cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data;
_cmsParametricCurvesCollection* fl; _cmsParametricCurvesCollection* fl;
if (Data == NULL) { if (Data == NULL) {
ParametricCurves = &DefaultCurves; ctx -> ParametricCurves = NULL;
return TRUE; return TRUE;
} }
fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(sizeof(_cmsParametricCurvesCollection)); fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection));
if (fl == NULL) return FALSE; if (fl == NULL) return FALSE;
// Copy the parameters // Copy the parameters
@ -97,8 +151,8 @@ cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Data)
memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number));
// Keep linked list // Keep linked list
fl ->Next = ParametricCurves; fl ->Next = ctx->ParametricCurves;
ParametricCurves = fl; ctx->ParametricCurves = fl;
// All is ok // All is ok
return TRUE; return TRUE;
@ -120,12 +174,24 @@ int IsInSet(int Type, _cmsParametricCurvesCollection* c)
// Search for the collection which contains a specific type // Search for the collection which contains a specific type
static static
_cmsParametricCurvesCollection *GetParametricCurveByType(int Type, int* index) _cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index)
{ {
_cmsParametricCurvesCollection* c; _cmsParametricCurvesCollection* c;
int Position; 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); Position = IsInSet(Type, c);
@ -222,14 +288,15 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntr
p ->Segments[i].SampledPoints = NULL; p ->Segments[i].SampledPoints = NULL;
c = GetParametricCurveByType(Segments[i].Type, NULL); c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL);
if (c != NULL) if (c != NULL)
p ->Evals[i] = c ->Evaluator; p ->Evals[i] = c ->Evaluator;
} }
} }
p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS); p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS);
return p; if (p->InterpParams != NULL)
return p;
Error: Error:
if (p -> Segments) _cmsFree(ContextID, p ->Segments); if (p -> Segments) _cmsFree(ContextID, p ->Segments);
@ -248,18 +315,28 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
switch (Type) { switch (Type) {
// X = Y ^ Gamma // X = Y ^ Gamma
case 1: case 1:
if (R < 0) if (R < 0) {
Val = 0;
if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)
Val = R;
else
Val = 0;
}
else else
Val = pow(R, Params[0]); Val = pow(R, Params[0]);
break; break;
// Type 1 Reversed: X = Y ^1/gamma // Type 1 Reversed: X = Y ^1/gamma
case -1: case -1:
if (R < 0) if (R < 0) {
Val = 0;
if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE)
Val = R;
else
Val = 0;
}
else else
Val = pow(R, 1/Params[0]); Val = pow(R, 1/Params[0]);
break; break;
@ -384,7 +461,7 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
if (e > 0) if (e > 0)
Val = pow(e, Params[0]) + Params[5]; Val = pow(e, Params[0]) + Params[5];
else else
Val = 0; Val = Params[5];
} }
else else
Val = R*Params[3] + Params[6]; Val = R*Params[3] + Params[6];
@ -419,7 +496,7 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
e = Params[1]*R + Params[2]; e = Params[1]*R + Params[2];
if (e < 0) if (e < 0)
Val = 0; Val = Params[3];
else else
Val = pow(e, Params[0]) + Params[3]; Val = pow(e, Params[0]) + Params[3];
break; break;
@ -439,7 +516,7 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
e = Params[2] * pow(R, Params[0]) + Params[3]; e = Params[2] * pow(R, Params[0]) + Params[3];
if (e <= 0) if (e <= 0)
Val = 0; Val = Params[4];
else else
Val = Params[1]*log10(e) + Params[4]; Val = Params[1]*log10(e) + Params[4];
break; break;
@ -505,7 +582,7 @@ cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R)
// Type == 0 means segment is sampled // Type == 0 means segment is sampled
if (g ->Segments[i].Type == 0) { 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; cmsFloat32Number Out;
// Setup the table (TODO: clean that) // Setup the table (TODO: clean that)
@ -523,6 +600,19 @@ cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R)
return MINUS_INF; 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 // Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the
// floating point description empty. // floating point description empty.
@ -577,20 +667,21 @@ cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID,
// Use a segmented curve to store the floating point table // Use a segmented curve to store the floating point table
cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]) cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[])
{ {
cmsCurveSegment Seg[2]; cmsCurveSegment Seg[3];
// Initialize segmented curve part up to 0 // A segmented tone curve should have function segments in the first and last positions
Seg[0].x0 = -1; // Initialize segmented curve part up to 0 to constant value = samples[0]
Seg[0].x0 = MINUS_INF;
Seg[0].x1 = 0; Seg[0].x1 = 0;
Seg[0].Type = 6; Seg[0].Type = 6;
Seg[0].Params[0] = 1; Seg[0].Params[0] = 1;
Seg[0].Params[1] = 0; Seg[0].Params[1] = 0;
Seg[0].Params[2] = 0; Seg[0].Params[2] = 0;
Seg[0].Params[3] = 0; Seg[0].Params[3] = values[0];
Seg[0].Params[4] = 0; Seg[0].Params[4] = 0;
// From zero to any // From zero to 1
Seg[1].x0 = 0; Seg[1].x0 = 0;
Seg[1].x1 = 1.0; Seg[1].x1 = 1.0;
Seg[1].Type = 0; Seg[1].Type = 0;
@ -598,7 +689,19 @@ cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cm
Seg[1].nGridPoints = nEntries; Seg[1].nGridPoints = nEntries;
Seg[1].SampledPoints = (cmsFloat32Number*) values; 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 // Parametric curves
@ -611,12 +714,12 @@ cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt
cmsCurveSegment Seg0; cmsCurveSegment Seg0;
int Pos = 0; int Pos = 0;
cmsUInt32Number size; cmsUInt32Number size;
_cmsParametricCurvesCollection* c = GetParametricCurveByType(Type, &Pos); _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos);
_cmsAssert(Params != NULL); _cmsAssert(Params != NULL);
if (c == 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; return NULL;
} }
@ -806,7 +909,10 @@ cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, con
_cmsAssert(InCurve != NULL); _cmsAssert(InCurve != NULL);
// Try to reverse it analytically whatever possible // 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, return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID,
-(InCurve -> Segments[0].Type), -(InCurve -> Segments[0].Type),
@ -941,7 +1047,7 @@ cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda
if (Tab == NULL) return FALSE; if (Tab == NULL) return FALSE;
if (cmsIsToneCurveLinear(Tab)) return FALSE; // Nothing to do if (cmsIsToneCurveLinear(Tab)) return TRUE; // Nothing to do
nItems = Tab -> nEntries; nItems = Tab -> nEntries;
@ -968,11 +1074,20 @@ cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda
if (z[i] == 0.) Zeros++; if (z[i] == 0.) Zeros++;
if (z[i] >= 65535.) Poles++; 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 (Zeros > (nItems / 3)) {
if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles 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 // Seems ok
for (i=0; i < nItems; i++) { for (i=0; i < nItems; i++) {
@ -1008,20 +1123,42 @@ cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t)
{ {
int n; int n;
int i, last; int i, last;
cmsBool lDescending;
_cmsAssert(t != NULL); _cmsAssert(t != NULL);
n = t ->nEntries; // Degenerated curves are monotonic? Ok, let's pass them
last = t ->Table16[n-1]; 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; last = t ->Table16[0];
else
last = t ->Table16[i];
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; return TRUE;

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -162,7 +162,7 @@ cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
out = ComputeKToLstar(ContextID, nPoints, 1, out = ComputeKToLstar(ContextID, nPoints, 1,
Intents + (nProfiles - 1), Intents + (nProfiles - 1),
hProfiles + (nProfiles - 1), &hProfiles [nProfiles - 1],
BPC + (nProfiles - 1), BPC + (nProfiles - 1),
AdaptationStates + (nProfiles - 1), AdaptationStates + (nProfiles - 1),
dwFlags); dwFlags);
@ -183,7 +183,6 @@ cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID,
// Make sure it is monotonic // Make sure it is monotonic
if (!cmsIsToneCurveMonotonic(KTone)) { if (!cmsIsToneCurveMonotonic(KTone)) {
cmsFreeToneCurve(KTone); cmsFreeToneCurve(KTone);
return NULL; return NULL;
} }
@ -221,13 +220,10 @@ int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number O
cmsFloat64Number dE1, dE2, ErrorRatio; cmsFloat64Number dE1, dE2, ErrorRatio;
// Assume in-gamut by default. // Assume in-gamut by default.
dE1 = 0.;
dE2 = 0;
ErrorRatio = 1.0; ErrorRatio = 1.0;
// Convert input to Lab // 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 // converts from PCS to colorant. This always
// does return in-gamut values, // does return in-gamut values,
@ -239,7 +235,7 @@ int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number O
memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab)); memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab));
// Try again, but this time taking Check as input // 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); cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1);
// Take difference of direct value // 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. // of course, many perceptual and saturation intents does not work in such way, but relativ. ones should.
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
cmsHPROFILE hProfiles[], cmsHPROFILE hProfiles[],
cmsBool BPC[], cmsBool BPC[],
cmsUInt32Number Intents[], cmsUInt32Number Intents[],
cmsFloat64Number AdaptationStates[], cmsFloat64Number AdaptationStates[],
cmsUInt32Number nGamutPCSposition, cmsUInt32Number nGamutPCSposition,
cmsHPROFILE hGamut) cmsHPROFILE hGamut)
{ {
cmsHPROFILE hLab; cmsHPROFILE hLab;
cmsPipeline* Gamut; cmsPipeline* Gamut;
cmsStage* CLUT; cmsStage* CLUT;
cmsUInt32Number dwFormat; cmsUInt32Number dwFormat;
GAMUTCHAIN Chain; GAMUTCHAIN Chain;
int nChannels, nGridpoints; int nChannels, nGridpoints;
cmsColorSpaceSignature ColorSpace; cmsColorSpaceSignature ColorSpace;
cmsUInt32Number i; cmsUInt32Number i;
cmsHPROFILE ProfileList[256]; cmsHPROFILE ProfileList[256];
cmsBool BPCList[256]; cmsBool BPCList[256];
cmsFloat64Number AdaptationList[256]; cmsFloat64Number AdaptationList[256];
cmsUInt32Number IntentList[256]; cmsUInt32Number IntentList[256];
memset(&Chain, 0, sizeof(GAMUTCHAIN)); memset(&Chain, 0, sizeof(GAMUTCHAIN));
if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) {
cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition);
return NULL; return NULL;
} }
hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
if (hLab == NULL) return NULL; if (hLab == NULL) return NULL;
// The figure of merit. On matrix-shaper profiles, should be almost zero as // The figure of merit. On matrix-shaper profiles, should be almost zero as
// the conversion is pretty exact. On LUT based profiles, different resolutions // the conversion is pretty exact. On LUT based profiles, different resolutions
// of input and output CLUT may result in differences. // of input and output CLUT may result in differences.
if (cmsIsMatrixShaper(hGamut)) { if (cmsIsMatrixShaper(hGamut)) {
Chain.Thereshold = 1.0; Chain.Thereshold = 1.0;
} }
else { else {
Chain.Thereshold = ERR_THERESHOLD; Chain.Thereshold = ERR_THERESHOLD;
} }
// Create a copy of parameters // Create a copy of parameters
for (i=0; i < nGamutPCSposition; i++) { for (i=0; i < nGamutPCSposition; i++) {
ProfileList[i] = hProfiles[i]; ProfileList[i] = hProfiles[i];
BPCList[i] = BPC[i]; BPCList[i] = BPC[i];
AdaptationList[i] = AdaptationStates[i]; AdaptationList[i] = AdaptationStates[i];
IntentList[i] = Intents[i]; IntentList[i] = Intents[i];
} }
// Fill Lab identity // Fill Lab identity
ProfileList[nGamutPCSposition] = hLab; ProfileList[nGamutPCSposition] = hLab;
BPCList[nGamutPCSposition] = 0; BPCList[nGamutPCSposition] = 0;
AdaptationList[nGamutPCSposition] = 1.0; AdaptationList[nGamutPCSposition] = 1.0;
Intents[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC;
ColorSpace = cmsGetColorSpace(hGamut); ColorSpace = cmsGetColorSpace(hGamut);
nChannels = cmsChannelsOf(ColorSpace); nChannels = cmsChannelsOf(ColorSpace);
nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
// 16 bits to Lab double // 16 bits to Lab double
Chain.hInput = cmsCreateExtendedTransform(ContextID, Chain.hInput = cmsCreateExtendedTransform(ContextID,
nGamutPCSposition + 1, nGamutPCSposition + 1,
ProfileList, ProfileList,
BPCList, BPCList,
Intents, IntentList,
AdaptationList, AdaptationList,
NULL, 0, NULL, 0,
dwFormat, TYPE_Lab_DBL, dwFormat, TYPE_Lab_DBL,
cmsFLAGS_NOCACHE); cmsFLAGS_NOCACHE);
// Does create the forward step. Lab double to device // Does create the forward step. Lab double to device
dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
Chain.hForward = cmsCreateTransformTHR(ContextID, Chain.hForward = cmsCreateTransformTHR(ContextID,
hLab, TYPE_Lab_DBL, hLab, TYPE_Lab_DBL,
hGamut, dwFormat, hGamut, dwFormat,
INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE); cmsFLAGS_NOCACHE);
// Does create the backwards step // Does create the backwards step
Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat,
hLab, TYPE_Lab_DBL, hLab, TYPE_Lab_DBL,
INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE); cmsFLAGS_NOCACHE);
// All ok? // All ok?
if (Chain.hForward && Chain.hReverse) { if (Chain.hInput && Chain.hForward && Chain.hReverse) {
// Go on, try to compute gamut LUT from PCS. This consist on a single channel containing // 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. // 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); // Free all needed stuff.
cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT); 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); // And return computed hull
} return Gamut;
}
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;
} }
// Total Area Coverage estimation ---------------------------------------------------------------- // Total Area Coverage estimation ----------------------------------------------------------------
@ -494,9 +493,9 @@ cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile)
GridPoints[2] = 74; GridPoints[2] = 74;
if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) { if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) {
bp.MaxTAC = 0; bp.MaxTAC = 0;
} }
cmsDeleteTransform(bp.hRoundTrip); 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -33,32 +33,57 @@
static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags);
// This is the default factory // 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 // Main plug-in entry
cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Data) cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data)
{ {
cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data;
_cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin);
if (Data == NULL) { if (Data == NULL) {
Interpolators = DefaultInterpolatorsFactory; ptr ->Interpolators = NULL;
return TRUE; return TRUE;
} }
// Set replacement functions // Set replacement functions
Interpolators = Plugin ->InterpolatorsFactory; ptr ->Interpolators = Plugin ->InterpolatorsFactory;
return TRUE; return TRUE;
} }
// Set the interpolation method // Set the interpolation method
cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p)
cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p)
{ {
// Invoke factory, possibly in the Plug-in _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin);
p ->Interpolation = Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags);
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 unsupported by the plug-in, go for the LittleCMS default.
// If happens only if an extern plug-in is being used // If happens only if an extern plug-in is being used
@ -69,6 +94,7 @@ cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p)
if (p ->Interpolation.Lerp16 == NULL) { if (p ->Interpolation.Lerp16 == NULL) {
return FALSE; return FALSE;
} }
return TRUE; return TRUE;
} }
@ -113,7 +139,7 @@ cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID,
p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; 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); cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan);
_cmsFree(ContextID, p); _cmsFree(ContextID, p);
return NULL; return NULL;
@ -186,6 +212,11 @@ void LinLerp1D(register const cmsUInt16Number Value[],
Output[0] = LinearInterp(rest, y0, y1); 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 // Floating-point version of 1D interpolation
static static
@ -198,13 +229,15 @@ void LinLerp1Dfloat(const cmsFloat32Number Value[],
int cell0, cell1; int cell0, cell1;
const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;
val2 = fclamp(Value[0]);
// if last value... // if last value...
if (Value[0] == 1.0) { if (val2 == 1.0) {
Output[0] = LutTable[p -> Domain[0]]; Output[0] = LutTable[p -> Domain[0]];
return; return;
} }
val2 = p -> Domain[0] * Value[0]; val2 *= p -> Domain[0];
cell0 = (int) floor(val2); cell0 = (int) floor(val2);
cell1 = (int) ceil(val2); cell1 = (int) ceil(val2);
@ -263,13 +296,15 @@ void Eval1InputFloat(const cmsFloat32Number Value[],
cmsUInt32Number OutChan; cmsUInt32Number OutChan;
const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table;
val2 = fclamp(Value[0]);
// if last value... // if last value...
if (Value[0] == 1.0) { if (val2 == 1.0) {
Output[0] = LutTable[p -> Domain[0]]; Output[0] = LutTable[p -> Domain[0]];
return; return;
} }
val2 = p -> Domain[0] * Value[0]; val2 *= p -> Domain[0];
cell0 = (int) floor(val2); cell0 = (int) floor(val2);
cell1 = (int) ceil(val2); cell1 = (int) ceil(val2);
@ -310,8 +345,8 @@ void BilinearInterpFloat(const cmsFloat32Number Input[],
dxy; dxy;
TotalOut = p -> nOutputs; TotalOut = p -> nOutputs;
px = Input[0] * p->Domain[0]; px = fclamp(Input[0]) * p->Domain[0];
py = Input[1] * p->Domain[1]; py = fclamp(Input[1]) * p->Domain[1];
x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0;
y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;
@ -425,20 +460,9 @@ void TrilinearInterpFloat(const cmsFloat32Number Input[],
TotalOut = p -> nOutputs; TotalOut = p -> nOutputs;
// We need some clipping here // We need some clipping here
px = Input[0]; px = fclamp(Input[0]) * p->Domain[0];
py = Input[1]; py = fclamp(Input[1]) * p->Domain[1];
pz = Input[2]; pz = fclamp(Input[2]) * p->Domain[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];
x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0;
y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0;
@ -580,20 +604,9 @@ void TetrahedralInterpFloat(const cmsFloat32Number Input[],
TotalOut = p -> nOutputs; TotalOut = p -> nOutputs;
// We need some clipping here // We need some clipping here
px = Input[0]; px = fclamp(Input[0]) * p->Domain[0];
py = Input[1]; py = fclamp(Input[1]) * p->Domain[1];
pz = Input[2]; pz = fclamp(Input[2]) * p->Domain[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];
x0 = (int) _cmsQuickFloor(px); rx = (px - (cmsFloat32Number) x0); x0 = (int) _cmsQuickFloor(px); rx = (px - (cmsFloat32Number) x0);
y0 = (int) _cmsQuickFloor(py); ry = (py - (cmsFloat32Number) y0); 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 static
void TetrahedralInterp16(register const cmsUInt16Number Input[], void TetrahedralInterp16(register const cmsUInt16Number Input[],
@ -683,99 +695,131 @@ void TetrahedralInterp16(register const cmsUInt16Number Input[],
register const cmsInterpParams* p) register const cmsInterpParams* p)
{ {
const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table; const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table;
cmsS15Fixed16Number fx, fy, fz; cmsS15Fixed16Number fx, fy, fz;
cmsS15Fixed16Number rx, ry, rz; cmsS15Fixed16Number rx, ry, rz;
int x0, y0, z0; int x0, y0, z0;
cmsS15Fixed16Number c0, c1, c2, c3, Rest; cmsS15Fixed16Number c0, c1, c2, c3, Rest;
cmsUInt32Number OutChan; cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; cmsUInt32Number TotalOut = p -> nOutputs;
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]); x0 = FIXED_TO_INT(fx);
fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); y0 = FIXED_TO_INT(fy);
fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); z0 = FIXED_TO_INT(fz);
x0 = FIXED_TO_INT(fx); rx = FIXED_REST_TO_INT(fx);
y0 = FIXED_TO_INT(fy); ry = FIXED_REST_TO_INT(fy);
z0 = FIXED_TO_INT(fz); 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; 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; 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; 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 LutTable = &LutTable[X0+Y0+Z0];
for (OutChan=0; OutChan < TotalOut; OutChan++) {
c0 = DENS(X0, Y0, Z0); // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))
// which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16
if (rx >= ry && ry >= rz) { // 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.
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);
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 } else if (rz >= rx) {
if (rz >= rx && rx >= ry) { X1 += Z1;
Y1 += X1;
c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); for (; TotalOut; TotalOut--) {
c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); c1 = LutTable[X1];
c3 = DENS(X0, Y0, Z1) - c0; c2 = LutTable[Y1];
c3 = LutTable[Z1];
} c0 = *LutTable++;
else c2 -= c1;
if (ry >= rx && rx >= rz) { c1 -= c3;
c3 -= c0;
c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
c2 = DENS(X0, Y1, Z0) - c0; *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); }
} else {
} Z1 += X1;
else Y1 += Z1;
if (ry >= rz && rz >= rx) { for (; TotalOut; TotalOut--) {
c1 = LutTable[X1];
c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); c2 = LutTable[Y1];
c2 = DENS(X0, Y1, Z0) - c0; c3 = LutTable[Z1];
c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); c0 = *LutTable++;
c2 -= c3;
} c3 -= c1;
else c1 -= c0;
if (rz >= ry && ry >= rx) { Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
*Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16);
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 {
if (rx >= rz) {
} X1 += Y1;
else { Z1 += X1;
c1 = c2 = c3 = 0; for (; TotalOut; TotalOut--) {
} c1 = LutTable[X1];
c2 = LutTable[Y1];
Rest = c1 * rx + c2 * ry + c3 * rz; c3 = LutTable[Z1];
c0 = *LutTable++;
Output[OutChan] = (cmsUInt16Number) c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)); 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]) #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
@ -784,7 +828,7 @@ void Eval4Inputs(register const cmsUInt16Number Input[],
register cmsUInt16Number Output[], register cmsUInt16Number Output[],
register const cmsInterpParams* p16) register const cmsInterpParams* p16)
{ {
const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; const cmsUInt16Number* LutTable;
cmsS15Fixed16Number fk; cmsS15Fixed16Number fk;
cmsS15Fixed16Number k0, rk; cmsS15Fixed16Number k0, rk;
int K0, K1; int K0, K1;
@ -979,8 +1023,7 @@ void Eval4InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1; cmsInterpParams p1;
pk = fclamp(Input[0]) * p->Domain[0];
pk = Input[0] * p->Domain[0];
k0 = _cmsQuickFloor(pk); k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0; rest = pk - (cmsFloat32Number) k0;
@ -1067,7 +1110,7 @@ void Eval5InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1; cmsInterpParams p1;
pk = Input[0] * p->Domain[0]; pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk); k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0; rest = pk - (cmsFloat32Number) k0;
@ -1154,7 +1197,7 @@ void Eval6InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1; cmsInterpParams p1;
pk = Input[0] * p->Domain[0]; pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk); k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0; rest = pk - (cmsFloat32Number) k0;
@ -1207,7 +1250,7 @@ void Eval7Inputs(register const cmsUInt16Number Input[],
K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));
p1 = *p16; 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; T = LutTable + K0;
p1.Table = T; p1.Table = T;
@ -1239,7 +1282,7 @@ void Eval7InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1; cmsInterpParams p1;
pk = Input[0] * p->Domain[0]; pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk); k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0; rest = pk - (cmsFloat32Number) k0;
@ -1324,7 +1367,7 @@ void Eval8InputsFloat(const cmsFloat32Number Input[],
cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];
cmsInterpParams p1; cmsInterpParams p1;
pk = Input[0] * p->Domain[0]; pk = fclamp(Input[0]) * p->Domain[0];
k0 = _cmsQuickFloor(pk); k0 = _cmsQuickFloor(pk);
rest = pk - (cmsFloat32Number) k0; rest = pk - (cmsFloat32Number) k0;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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); Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag);
if (Tag != NULL) { if (Tag != NULL) {
*Dest = *Tag; *Dest = *Tag;
return TRUE; return TRUE;
} }
@ -121,7 +120,7 @@ cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile)
return TRUE; 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; if (GrayTRC == NULL) return NULL;
Lut = cmsPipelineAlloc(ContextID, 1, 3); Lut = cmsPipelineAlloc(ContextID, 1, 3);
if (Lut == NULL) return NULL; if (Lut == NULL)
goto Error;
if (cmsGetPCS(hProfile) == cmsSigLabData) { if (cmsGetPCS(hProfile) == cmsSigLabData) {
@ -175,28 +175,35 @@ cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile)
EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
if (EmptyTab == NULL) { if (EmptyTab == NULL)
goto Error;
cmsPipelineFree(Lut);
return NULL;
}
LabCurves[0] = GrayTRC; LabCurves[0] = GrayTRC;
LabCurves[1] = EmptyTab; LabCurves[1] = EmptyTab;
LabCurves[2] = EmptyTab; LabCurves[2] = EmptyTab;
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)); if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) ||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves)); !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) {
cmsFreeToneCurve(EmptyTab);
goto Error;
}
cmsFreeToneCurve(EmptyTab); cmsFreeToneCurve(EmptyTab);
} }
else { 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; return Lut;
Error:
cmsFreeToneCurve(GrayTRC);
cmsPipelineFree(Lut);
return NULL;
} }
// RGB Matrix shaper // RGB Matrix shaper
@ -230,15 +237,76 @@ cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile)
Lut = cmsPipelineAlloc(ContextID, 3, 3); Lut = cmsPipelineAlloc(ContextID, 3, 3);
if (Lut != NULL) { if (Lut != NULL) {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)); if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) ||
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL)); !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; 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 // 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) cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
{ {
cmsTagTypeSignature OriginalType; cmsTagTypeSignature OriginalType;
@ -246,40 +314,78 @@ cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
cmsTagSignature tagFloat = Device2PCSFloat[Intent]; cmsTagSignature tagFloat = Device2PCSFloat[Intent];
cmsContext ContextID = cmsGetProfileContextID(hProfile); 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 cmsPipeline* Lut;
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
}
// Revert to perceptual if no tag is found if (nc == NULL) return NULL;
if (!cmsIsTag(hProfile, tag16)) {
tag16 = Device2PCS16[0];
}
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 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
// First read the tag cmsPipelineFree(Lut);
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); return NULL;
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));
return Lut; 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 // Lut was not found, try to create a matrix-shaper
// Check if this is a grayscale profile. // Check if this is a grayscale profile.
@ -322,21 +428,27 @@ cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
if (cmsGetPCS(hProfile) == cmsSigLabData) { 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 { 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); cmsFreeToneCurve(RevGrayTRC);
return Lut; return Lut;
Error:
cmsFreeToneCurve(RevGrayTRC);
cmsPipelineFree(Lut);
return NULL;
} }
static static
cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
{ {
@ -378,12 +490,26 @@ cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile)
Lut = cmsPipelineAlloc(ContextID, 3, 3); Lut = cmsPipelineAlloc(ContextID, 3, 3);
if (Lut != NULL) { if (Lut != NULL) {
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)); // Note that it is certainly possible a single profile would have a LUT based
cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes)); // 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); cmsFreeToneCurveTriple(InvShapes);
return Lut; return Lut;
Error:
cmsFreeToneCurveTriple(InvShapes);
cmsPipelineFree(Lut);
return NULL;
} }
@ -402,11 +528,56 @@ void ChangeInterpolationToTrilinear(cmsPipeline* Lut)
_cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data;
CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; 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 // Create an output MPE LUT from agiven profile. Version mismatches are handled here
cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent) cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
{ {
@ -415,62 +586,117 @@ cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
cmsTagSignature tagFloat = PCS2DeviceFloat[Intent]; cmsTagSignature tagFloat = PCS2DeviceFloat[Intent];
cmsContext ContextID = cmsGetProfileContextID(hProfile); cmsContext ContextID = cmsGetProfileContextID(hProfile);
if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
// Floating point LUT are always V4, so no adjustment is required if (Intent != -1) {
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
}
// Revert to perceptual if no tag is found if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
if (!cmsIsTag(hProfile, tag16)) {
tag16 = PCS2Device16[0];
}
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 if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
if (Lut == NULL) return NULL;
// After reading it, we have info about the original type // Check profile version and LUT type. Do the necessary adjustments if needed
OriginalType = _cmsGetTagTrueType(hProfile, tag16);
// The profile owns the Lut, so we need to copy it // First read the tag
Lut = cmsPipelineDup(Lut); cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
if (Lut == NULL) return NULL; if (Lut == NULL) return NULL;
// Now it is time for a controversial stuff. I found that for 3D LUTS using // After reading it, we have info about the original type
// Lab used as indexer space, trilinear interpolation should be used OriginalType = _cmsGetTagTrueType(hProfile, tag16);
if (cmsGetPCS(hProfile) == cmsSigLabData)
ChangeInterpolationToTrilinear(Lut); // 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; return Lut;
Error:
// Add a matrix for conversion V4 to V2 Lab PCS cmsPipelineFree(Lut);
cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)); return NULL;
return Lut; }
} }
// Lut not found, try to create a matrix-shaper // Lut not found, try to create a matrix-shaper
// Check if this is a grayscale profile. // Check if this is a grayscale profile.
if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
// if so, build appropiate conversion tables. // if so, build appropiate conversion tables.
// The tables are the PCS iluminant, scaled across GrayTRC // The tables are the PCS iluminant, scaled across GrayTRC
return BuildGrayOutputPipeline(hProfile); 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); 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 // This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
// tag name here may default to AToB0 // tag name here may default to AToB0
cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent) cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
@ -481,10 +707,36 @@ cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
cmsTagSignature tagFloat = Device2PCSFloat[Intent]; cmsTagSignature tagFloat = Device2PCSFloat[Intent];
cmsContext ContextID = cmsGetProfileContextID(hProfile); 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 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
// Floating point LUT are always V4, no adjustment is required // Floating point LUT are always V
return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); return _cmsReadFloatDevicelinkTag(hProfile, tagFloat);
} }
tagFloat = Device2PCSFloat[0]; tagFloat = Device2PCSFloat[0];
@ -509,10 +761,10 @@ cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
Lut = cmsPipelineDup(Lut); Lut = cmsPipelineDup(Lut);
if (Lut == NULL) return NULL; if (Lut == NULL) return NULL;
// Now it is time for a controversial stuff. I found that for 3D LUTS using // 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 // Lab used as indexer space, trilinear interpolation should be used
if (cmsGetColorSpace(hProfile) == cmsSigLabData) if (cmsGetPCS(hProfile) == cmsSigLabData)
ChangeInterpolationToTrilinear(Lut); ChangeInterpolationToTrilinear(Lut);
// After reading it, we have info about the original type // After reading it, we have info about the original type
OriginalType = _cmsGetTagTrueType(hProfile, tag16); OriginalType = _cmsGetTagTrueType(hProfile, tag16);
@ -522,17 +774,21 @@ cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
// Here it is possible to get Lab on both sides // Here it is possible to get Lab on both sides
if (cmsGetPCS(hProfile) == cmsSigLabData) { if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)); if(!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
goto Error2;
} }
if (cmsGetColorSpace(hProfile) == cmsSigLabData) { if (cmsGetPCS(hProfile) == cmsSigLabData) {
cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)); if(!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
goto Error2;
} }
return Lut; 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 // 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. // create qa unique structure holding both. Shame on ICC to store things in such complicated way.
cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile) cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
{ {
cmsSEQ* ProfileSeq; cmsSEQ* ProfileSeq;
@ -636,12 +891,13 @@ cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile)
NewSeq = cmsDupProfileSequenceDescription(ProfileSeq); NewSeq = cmsDupProfileSequenceDescription(ProfileSeq);
// Ok, proceed to the mixing // 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)); memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID));
NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description); NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
}
} }
return NewSeq; return NewSeq;
} }

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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++) { for (i=0; i < n; i++) {
// Get asked type // Get asked type
Type = va_arg(args, cmsStageSignature); Type = (cmsStageSignature)va_arg(args, cmsStageSignature);
if (mpe ->Type != Type) { if (mpe ->Type != Type) {
va_end(args); // Mismatch. We are done. va_end(args); // Mismatch. We are done.
@ -235,10 +235,10 @@ Error:
if (NewElem ->TheCurves != NULL) { if (NewElem ->TheCurves != NULL) {
for (i=0; i < NewElem ->nCurves; i++) { for (i=0; i < NewElem ->nCurves; i++) {
if (NewElem ->TheCurves[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); _cmsFree(mpe ->ContextID, NewElem);
return NULL; return NULL;
} }
@ -256,7 +256,7 @@ cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Numbe
EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL );
if (NewMPE == NULL) return NULL; if (NewMPE == NULL) return NULL;
NewElem = (_cmsStageToneCurvesData*) _cmsMalloc(ContextID, sizeof(_cmsStageToneCurvesData)); NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData));
if (NewElem == NULL) { if (NewElem == NULL) {
cmsStageFree(NewMPE); cmsStageFree(NewMPE);
return NULL; return NULL;
@ -284,9 +284,10 @@ cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Numbe
cmsStageFree(NewMPE); cmsStageFree(NewMPE);
return NULL; return NULL;
} }
} }
return NewMPE; return NewMPE;
} }
@ -362,6 +363,8 @@ static
void MatrixElemTypeFree(cmsStage* mpe) void MatrixElemTypeFree(cmsStage* mpe)
{ {
_cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
if (Data == NULL)
return;
if (Data ->Double) if (Data ->Double)
_cmsFree(mpe ->ContextID, Data ->Double); _cmsFree(mpe ->ContextID, Data ->Double);
@ -496,10 +499,15 @@ void* CLUTElemDup(cmsStage* mpe)
if (Data ->Tab.T) { if (Data ->Tab.T) {
if (Data ->HasFloatValues) if (Data ->HasFloatValues) {
NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); 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)); 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, NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID,
@ -508,8 +516,14 @@ void* CLUTElemDup(cmsStage* mpe)
Data ->Params ->nOutputs, Data ->Params ->nOutputs,
NewElem ->Tab.T, NewElem ->Tab.T,
Data ->Params ->dwFlags); Data ->Params ->dwFlags);
if (NewElem->Params != NULL)
return (void*) NewElem; 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; _cmsStageCLutData* NewElem;
cmsStage* NewMPE; 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, NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL );
if (NewMPE == NULL) return NULL; if (NewMPE == NULL) return NULL;
NewElem = (_cmsStageCLutData*) _cmsMalloc(ContextID, sizeof(_cmsStageCLutData)); NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
if (NewElem == NULL) { if (NewElem == NULL) {
cmsStageFree(NewMPE); cmsStageFree(NewMPE);
return NULL; return NULL;
@ -599,7 +620,6 @@ cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID,
for (i=0; i < MAX_INPUT_DIMENSIONS; i++) for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
Dimensions[i] = nGridPoints; Dimensions[i] = nGridPoints;
return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table);
} }
@ -630,12 +650,17 @@ cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const c
_cmsAssert(clutPoints != NULL); _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, NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL);
if (NewMPE == NULL) return NULL; if (NewMPE == NULL) return NULL;
NewElem = (_cmsStageCLutData*) _cmsMalloc(ContextID, sizeof(_cmsStageCLutData)); NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
if (NewElem == NULL) { if (NewElem == NULL) {
cmsStageFree(NewMPE); cmsStageFree(NewMPE);
return NULL; return NULL;
@ -644,7 +669,7 @@ cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const c
NewMPE ->Data = (void*) NewElem; NewMPE ->Data = (void*) NewElem;
// There is a potential integer overflow on conputing n and nEntries. // 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; NewElem -> HasFloatValues = TRUE;
if (n == 0) { 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); NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT);
if (NewElem ->Params == NULL) { if (NewElem ->Params == NULL) {
cmsStageFree(NewMPE); cmsStageFree(NewMPE);
return NULL; return NULL;
} }
return NewMPE; return NewMPE;
} }
@ -731,15 +752,22 @@ cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, v
int i, t, nTotalPoints, index, rest; int i, t, nTotalPoints, index, rest;
int nInputs, nOutputs; int nInputs, nOutputs;
cmsUInt32Number* nSamples; cmsUInt32Number* nSamples;
cmsUInt16Number In[cmsMAXCHANNELS], Out[MAX_STAGE_CHANNELS]; cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
_cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; _cmsStageCLutData* clut;
if (mpe == NULL) return FALSE;
clut = (_cmsStageCLutData*) mpe->Data;
if (clut == NULL) return FALSE;
nSamples = clut->Params ->nSamples; nSamples = clut->Params ->nSamples;
nInputs = clut->Params ->nInputs; nInputs = clut->Params ->nInputs;
nOutputs = clut->Params ->nOutputs; 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; if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
nTotalPoints = CubeSize(nSamples, nInputs); nTotalPoints = CubeSize(nSamples, nInputs);
@ -786,14 +814,16 @@ cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler
int i, t, nTotalPoints, index, rest; int i, t, nTotalPoints, index, rest;
int nInputs, nOutputs; int nInputs, nOutputs;
cmsUInt32Number* nSamples; cmsUInt32Number* nSamples;
cmsFloat32Number In[cmsMAXCHANNELS], Out[MAX_STAGE_CHANNELS]; cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
_cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;
nSamples = clut->Params ->nSamples; nSamples = clut->Params ->nSamples;
nInputs = clut->Params ->nInputs; nInputs = clut->Params ->nInputs;
nOutputs = clut->Params ->nOutputs; 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; if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
nTotalPoints = CubeSize(nSamples, nInputs); nTotalPoints = CubeSize(nSamples, nInputs);
@ -975,6 +1005,7 @@ cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID)
mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable);
cmsFreeToneCurveTriple(LabTable); cmsFreeToneCurveTriple(LabTable);
if (mpe == NULL) return NULL;
mpe ->Implements = cmsSigLabV2toV4; mpe ->Implements = cmsSigLabV2toV4;
return mpe; 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 // Type cmsSigXYZ2LabElemType
// ******************************************************************************** // ********************************************************************************
@ -1117,12 +1231,22 @@ cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe)
NULL); NULL);
if (NewMPE == NULL) return 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; NewMPE ->Data = NULL;
}
return NewMPE; return NewMPE;
} }
@ -1229,14 +1353,21 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In
return NewLUT; return NewLUT;
} }
cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut)
{
_cmsAssert(lut != NULL);
return lut ->ContextID;
}
cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut) cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut)
{ {
_cmsAssert(lut != NULL);
return lut ->InputChannels; return lut ->InputChannels;
} }
cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut) cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut)
{ {
_cmsAssert(lut != NULL);
return lut ->OutputChannels; return lut ->OutputChannels;
} }
@ -1264,6 +1395,7 @@ void CMSEXPORT cmsPipelineFree(cmsPipeline* lut)
// Default to evaluate the LUT on 16 bit-basis. // Default to evaluate the LUT on 16 bit-basis.
void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut)
{ {
_cmsAssert(lut != NULL);
lut ->Eval16Fn(In, Out, lut->Data); 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. // Does evaluate the LUT on cmsFloat32Number-basis.
void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut)
{ {
_cmsAssert(lut != NULL);
lut ->EvalFloatFn(In, Out, lut); lut ->EvalFloatFn(In, Out, lut);
} }
@ -1286,6 +1419,8 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
if (lut == NULL) return NULL; if (lut == NULL) return NULL;
NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels); NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels);
if (NewLUT == NULL) return NULL;
for (mpe = lut ->Elements; for (mpe = lut ->Elements;
mpe != NULL; mpe != NULL;
mpe = mpe ->Next) { mpe = mpe ->Next) {
@ -1308,8 +1443,10 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut)
Anterior = NewMPE; Anterior = NewMPE;
} }
NewLUT ->DupDataFn = lut ->DupDataFn; NewLUT ->Eval16Fn = lut ->Eval16Fn;
NewLUT ->FreeDataFn = lut ->FreeDataFn; NewLUT ->EvalFloatFn = lut ->EvalFloatFn;
NewLUT ->DupDataFn = lut ->DupDataFn;
NewLUT ->FreeDataFn = lut ->FreeDataFn;
if (NewLUT ->DupDataFn != NULL) if (NewLUT ->DupDataFn != NULL)
NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data); 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; cmsStage* Anterior = NULL, *pt;
_cmsAssert(lut != NULL); if (lut == NULL || mpe == NULL)
_cmsAssert(mpe != NULL); return FALSE;
switch (loc) { switch (loc) {
@ -1351,9 +1488,11 @@ void CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStag
} }
break; break;
default:; default:;
return FALSE;
} }
BlessLUT(lut); BlessLUT(lut);
return TRUE;
} }
// Unlink an element and return the pointer to it // 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 // Concatenate two LUT into a new single one
cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)
{ {
cmsStage* mpe, *NewMPE; cmsStage* mpe;
// If both LUTS does not have elements, we need to inherit // If both LUTS does not have elements, we need to inherit
// the number of channels // the number of channels
@ -1430,17 +1569,12 @@ cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2)
mpe = mpe ->Next) { mpe = mpe ->Next) {
// We have to dup each element // We have to dup each element
NewMPE = cmsStageDup(mpe); if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe)))
return FALSE;
if (NewMPE == NULL) {
return FALSE;
}
cmsPipelineInsertStage(l1, cmsAT_END, NewMPE);
} }
BlessLUT(l1); BlessLUT(l1);
return TRUE; return TRUE;
} }
@ -1479,13 +1613,13 @@ cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut)
return n; 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. // 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, void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut,
_cmsOPTeval16Fn Eval16, _cmsOPTeval16Fn Eval16,
void* PrivateData, void* PrivateData,
_cmsOPTfreeDataFn FreePrivateDataFn, _cmsFreeUserDataFn FreePrivateDataFn,
_cmsOPTdupDataFn DupPrivateDataFn) _cmsDupUserDataFn DupPrivateDataFn)
{ {
Lut ->Eval16Fn = Eval16; Lut ->Eval16Fn = Eval16;
@ -1570,16 +1704,11 @@ cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[],
cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; cmsFloat32Number fx[4], x[4], xd[4], fxd[4];
cmsVEC3 tmp, tmp2; cmsVEC3 tmp, tmp2;
cmsMAT3 Jacobian; cmsMAT3 Jacobian;
cmsFloat64Number LastResult[4];
// Only 3->3 and 4->3 are supported // Only 3->3 and 4->3 are supported
if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE;
if (lut ->OutputChannels != 3) 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 // Take the hint as starting point if specified
if (Hint == NULL) { if (Hint == NULL) {
@ -1663,3 +1792,4 @@ cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[],
return TRUE; return TRUE;
} }

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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* Icc = (_cmsICCPROFILE*) hProfile;
_cmsICCPROFILE Keep; _cmsICCPROFILE Keep;
_cmsAssert(hProfile != NULL); _cmsAssert(hProfile != NULL);
ContextID = cmsGetProfileContextID(hProfile); ContextID = cmsGetProfileContextID(hProfile);
@ -309,7 +309,7 @@ cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile)
Error: Error:
// Free resources as something went wrong // 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); if (Mem != NULL) _cmsFree(ContextID, Mem);
memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
return FALSE; return FALSE;

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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) cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a)
{ {
cmsMAT3 Identity; cmsMAT3 Identity;
int i, j; int i, j;
_cmsMAT3identity(&Identity); _cmsMAT3identity(&Identity);
for (i=0; i < 3; i++) for (i=0; i < 3; i++)
for (j=0; j < 3; j++) for (j=0; j < 3; j++)
if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE; 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -32,29 +32,29 @@
// Allocates an empty multi localizad unicode object // Allocates an empty multi localizad unicode object
cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems) cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
{ {
cmsMLU* mlu; cmsMLU* mlu;
// nItems should be positive if given // nItems should be positive if given
if (nItems <= 0) nItems = 2; if (nItems <= 0) nItems = 2;
// Create the container // Create the container
mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU)); mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
if (mlu == NULL) return NULL; if (mlu == NULL) return NULL;
mlu ->ContextID = ContextID; mlu ->ContextID = ContextID;
// Create entry array // Create entry array
mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry)); mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
if (mlu ->Entries == NULL) { if (mlu ->Entries == NULL) {
_cmsFree(ContextID, mlu); _cmsFree(ContextID, mlu);
return NULL; return NULL;
} }
// Ok, keep indexes up to date // Ok, keep indexes up to date
mlu ->AllocatedEntries = nItems; mlu ->AllocatedEntries = nItems;
mlu ->UsedEntries = 0; mlu ->UsedEntries = 0;
return mlu; return mlu;
} }
@ -62,48 +62,48 @@ cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
static static
cmsBool GrowMLUpool(cmsMLU* mlu) cmsBool GrowMLUpool(cmsMLU* mlu)
{ {
cmsUInt32Number size; cmsUInt32Number size;
void *NewPtr; void *NewPtr;
// Sanity check // Sanity check
if (mlu == NULL) return FALSE; if (mlu == NULL) return FALSE;
if (mlu ->PoolSize == 0) if (mlu ->PoolSize == 0)
size = 256; size = 256;
else else
size = mlu ->PoolSize * 2; size = mlu ->PoolSize * 2;
// Check for overflow // Check for overflow
if (size < mlu ->PoolSize) return FALSE; if (size < mlu ->PoolSize) return FALSE;
// Reallocate the pool // Reallocate the pool
NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size); NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
if (NewPtr == NULL) return FALSE; if (NewPtr == NULL) return FALSE;
mlu ->MemPool = NewPtr; mlu ->MemPool = NewPtr;
mlu ->PoolSize = size; 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 static
cmsBool GrowMLUtable(cmsMLU* mlu) cmsBool GrowMLUtable(cmsMLU* mlu)
{ {
int AllocatedEntries; int AllocatedEntries;
_cmsMLUentry *NewPtr; _cmsMLUentry *NewPtr;
// Sanity check // Sanity check
if (mlu == NULL) return FALSE; if (mlu == NULL) return FALSE;
AllocatedEntries = mlu ->AllocatedEntries * 2; AllocatedEntries = mlu ->AllocatedEntries * 2;
// Check for overflow // Check for overflow
if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE; if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
// Reallocate the memory // Reallocate the memory
NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry)); NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
if (NewPtr == NULL) return FALSE; if (NewPtr == NULL) return FALSE;
@ -120,17 +120,17 @@ int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number Co
{ {
int i; int i;
// Sanity check // Sanity check
if (mlu == NULL) return -1; if (mlu == NULL) return -1;
// Iterate whole table // Iterate whole table
for (i=0; i < mlu ->UsedEntries; i++) { for (i=0; i < mlu ->UsedEntries; i++) {
if (mlu ->Entries[i].Country == CountryCode && if (mlu ->Entries[i].Country == CountryCode &&
mlu ->Entries[i].Language == LanguageCode) return i; mlu ->Entries[i].Language == LanguageCode) return i;
} }
// Not found // Not found
return -1; return -1;
} }
@ -143,8 +143,8 @@ cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
cmsUInt32Number Offset; cmsUInt32Number Offset;
cmsUInt8Number* Ptr; cmsUInt8Number* Ptr;
// Sanity check // Sanity check
if (mlu == NULL) return FALSE; if (mlu == NULL) return FALSE;
// Is there any room available? // Is there any room available?
if (mlu ->UsedEntries >= mlu ->AllocatedEntries) { 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! if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed!
// Check for size // Check for size
while ((mlu ->PoolSize - mlu ->PoolUsed) < size) { while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
if (!GrowMLUpool(mlu)) return FALSE; if (!GrowMLUpool(mlu)) return FALSE;
} }
Offset = mlu ->PoolUsed; Offset = mlu ->PoolUsed;
Ptr = (cmsUInt8Number*) mlu ->MemPool; Ptr = (cmsUInt8Number*) mlu ->MemPool;
if (Ptr == NULL) return FALSE; if (Ptr == NULL) return FALSE;
// Set the entry // Set the entry
memmove(Ptr + Offset, Block, size); memmove(Ptr + Offset, Block, size);
mlu ->PoolUsed += size; mlu ->PoolUsed += size;
@ -209,7 +209,7 @@ cmsUInt32Number mywcslen(const wchar_t *s)
{ {
const wchar_t *p; const wchar_t *p;
p = s; p = s;
while (*p) while (*p)
p++; p++;
@ -225,7 +225,7 @@ cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char
cmsUInt32Number len; cmsUInt32Number len;
if (mlu == NULL) return FALSE; if (mlu == NULL) return FALSE;
if (WideString == NULL) return FALSE; if (WideString == NULL) return FALSE;
len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t); len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t);
return AddMLUBlock(mlu, len, WideString, Lang, Cntry); 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 // Duplicating a MLU is as easy as copying all members
cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu) cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)
{ {
cmsMLU* NewMlu = NULL; cmsMLU* NewMlu = NULL;
// Duplicating a NULL obtains a NULL // Duplicating a NULL obtains a NULL
if (mlu == NULL) return NULL; if (mlu == NULL) return NULL;
NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries); NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
if (NewMlu == NULL) return NULL; if (NewMlu == NULL) return NULL;
// Should never happen // Should never happen
if (NewMlu ->AllocatedEntries < mlu ->UsedEntries) if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
goto Error; goto Error;
// Sanitize... // Sanitize...
if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error; if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error;
memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry)); memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
NewMlu ->UsedEntries = mlu ->UsedEntries; NewMlu ->UsedEntries = mlu ->UsedEntries;
// The MLU may be empty // The MLU may be empty
if (mlu ->PoolUsed == 0) { if (mlu ->PoolUsed == 0) {
NewMlu ->MemPool = NULL; NewMlu ->MemPool = NULL;
} }
else { else {
// It is not empty // It is not empty
NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed); NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed);
if (NewMlu ->MemPool == NULL) goto Error; 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); memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
NewMlu ->PoolUsed = mlu ->PoolUsed; NewMlu ->PoolUsed = mlu ->PoolUsed;
return NewMlu; return NewMlu;
Error: Error:
if (NewMlu != NULL) cmsMLUfree(NewMlu); if (NewMlu != NULL) cmsMLUfree(NewMlu);
return NULL; return NULL;
} }
// Free any used memory // Free any used memory
void CMSEXPORT cmsMLUfree(cmsMLU* mlu) void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
{ {
if (mlu) { if (mlu) {
if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries); if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool); 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. // the Language. If none is found, first entry is used instead.
static static
const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
cmsUInt32Number *len, cmsUInt32Number *len,
cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode) cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
{ {
int i; int i;
int Best = -1; int Best = -1;
_cmsMLUentry* v; _cmsMLUentry* v;
if (mlu == NULL) return NULL; if (mlu == NULL) return NULL;
@ -316,8 +316,8 @@ const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
if (v -> Country == CountryCode) { if (v -> Country == CountryCode) {
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
if (len != NULL) *len = v ->Len; if (len != NULL) *len = v ->Len;
@ -330,30 +330,30 @@ const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
if (Best == -1) if (Best == -1)
Best = 0; Best = 0;
v = mlu ->Entries + Best; v = mlu ->Entries + Best;
if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
if (len != NULL) *len = v ->Len; 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 // Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3], const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize) char* Buffer, cmsUInt32Number BufferSize)
{ {
const wchar_t *Wide; const wchar_t *Wide;
cmsUInt32Number StrLen = 0; cmsUInt32Number StrLen = 0;
cmsUInt32Number ASCIIlen, i; cmsUInt32Number ASCIIlen, i;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
// Sanitize // Sanitize
if (mlu == NULL) return 0; if (mlu == NULL) return 0;
// Get WideChar // Get WideChar
@ -381,23 +381,23 @@ cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
Buffer[i] = (char) Wide[i]; Buffer[i] = (char) Wide[i];
} }
// We put a termination "\0" // We put a termination "\0"
Buffer[ASCIIlen] = 0; Buffer[ASCIIlen] = 0;
return ASCIIlen + 1; return ASCIIlen + 1;
} }
// Obtain a wide representation of the MLU, on depending on current locale settings // Obtain a wide representation of the MLU, on depending on current locale settings
cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3], const char LanguageCode[3], const char CountryCode[3],
wchar_t* Buffer, cmsUInt32Number BufferSize) wchar_t* Buffer, cmsUInt32Number BufferSize)
{ {
const wchar_t *Wide; const wchar_t *Wide;
cmsUInt32Number StrLen = 0; cmsUInt32Number StrLen = 0;
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
// Sanitize // Sanitize
if (mlu == NULL) return 0; if (mlu == NULL) return 0;
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
@ -414,7 +414,7 @@ cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
StrLen = BufferSize - + sizeof(wchar_t); StrLen = BufferSize - + sizeof(wchar_t);
memmove(Buffer, Wide, StrLen); memmove(Buffer, Wide, StrLen);
Buffer[StrLen / sizeof(wchar_t)] = 0; Buffer[StrLen / sizeof(wchar_t)] = 0;
return StrLen + sizeof(wchar_t); return StrLen + sizeof(wchar_t);
} }
@ -422,27 +422,56 @@ cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
// Get also the language and country // Get also the language and country
CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3], const char LanguageCode[3], const char CountryCode[3],
char ObtainedLanguage[3], char ObtainedCountry[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 Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
cmsUInt16Number ObtLang, ObtCode; cmsUInt16Number ObtLang, ObtCode;
// Sanitize // Sanitize
if (mlu == NULL) return FALSE; if (mlu == NULL) return FALSE;
Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode); 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 *)ObtainedLanguage = _cmsAdjustEndianess16(ObtLang);
*(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode); *(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode);
ObtainedLanguage[2] = ObtainedCountry[2] = 0; ObtainedLanguage[2] = ObtainedCountry[2] = 0;
return TRUE; 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) while (v -> Allocated < n)
GrowNamedColorList(v); GrowNamedColorList(v);
strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)); strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)); strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);
v->Prefix[32] = v->Suffix[32] = 0;
v -> ColorantCount = ColorantCount; v -> ColorantCount = ColorantCount;
return v; return v;
@ -498,8 +529,9 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn
// Free a list // Free a list
void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v) void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)
{ {
if (v == NULL) return;
if (v ->List) _cmsFree(v ->ContextID, v ->List); if (v ->List) _cmsFree(v ->ContextID, v ->List);
if (v) _cmsFree(v ->ContextID, v); _cmsFree(v ->ContextID, v);
} }
cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
@ -543,9 +575,12 @@ cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,
for (i=0; i < 3; i++) for (i=0; i < 3; i++)
NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i]; NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i];
if (Name != NULL) if (Name != NULL) {
strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name,
sizeof(NamedColorList ->List[NamedColorList ->nColors].Name)); strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);
NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;
}
else else
NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
@ -618,6 +653,24 @@ void* DupNamedColorList(cmsStage* mpe)
return cmsDupNamedColorList(List); 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 static
void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) 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 // Named color lookup element
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList) cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
{ {
return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
cmsSigNamedColorElemType, cmsSigNamedColorElemType,
1, 3, 1, UsePCS ? 3 : NamedColorList ->ColorantCount,
EvalNamedColor, UsePCS ? EvalNamedColorPCS : EvalNamedColor,
DupNamedColorList, DupNamedColorList,
FreeNamedColorList, FreeNamedColorList,
cmsDupNamedColorList(NamedColorList)); cmsDupNamedColorList(NamedColorList));
} }
@ -680,6 +733,10 @@ cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUI
Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
Seq -> n = n; Seq -> n = n;
if (Seq -> seq == NULL) {
_cmsFree(ContextID, Seq);
return NULL;
}
for (i=0; i < n; i++) { for (i=0; i < n; i++) {
Seq -> seq[i].Manufacturer = NULL; Seq -> seq[i].Manufacturer = NULL;
@ -745,6 +802,128 @@ Error:
return NULL; 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -53,10 +53,6 @@ typedef struct {
int nInputs; int nInputs;
int nOutputs; 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 _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance
cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS];
@ -174,8 +170,6 @@ cmsBool PreOptimize(cmsPipeline* Lut)
{ {
cmsBool AnyOpt = FALSE, Opt; cmsBool AnyOpt = FALSE, Opt;
AnyOpt = FALSE;
do { do {
Opt = FALSE; Opt = FALSE;
@ -195,6 +189,12 @@ cmsBool PreOptimize(cmsPipeline* Lut)
// Remove V2 to V4 followed by V4 to V2 // Remove V2 to V4 followed by V4 to V2
Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); 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; if (Opt) AnyOpt = TRUE;
} while (Opt); } while (Opt);
@ -219,6 +219,7 @@ void PrelinEval16(register const cmsUInt16Number Input[],
{ {
Prelin16Data* p16 = (Prelin16Data*) D; Prelin16Data* p16 = (Prelin16Data*) D;
cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS];
cmsUInt16Number StageDEF[cmsMAXCHANNELS];
int i; int i;
for (i=0; i < p16 ->nInputs; 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 ->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++) { 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; Prelin16Data* p16 = (Prelin16Data*) ptr;
_cmsFree(ContextID, p16 ->StageDEF);
_cmsFree(ContextID, p16 ->EvalCurveOut16); _cmsFree(ContextID, p16 ->EvalCurveOut16);
_cmsFree(ContextID, p16 ->ParamsCurveOut16); _cmsFree(ContextID, p16 ->ParamsCurveOut16);
@ -255,7 +255,6 @@ void* Prelin16dup(cmsContext ContextID, const void* ptr)
if (Duped == NULL) return NULL; if (Duped == NULL) return NULL;
Duped ->StageDEF = _cmsCalloc(ContextID, p16 ->nOutputs, sizeof(cmsUInt16Number));
Duped ->EvalCurveOut16 = _cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16)); Duped ->EvalCurveOut16 = _cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16));
Duped ->ParamsCurveOut16 = _cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* )); Duped ->ParamsCurveOut16 = _cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* ));
@ -270,7 +269,7 @@ Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
int nOutputs, cmsToneCurve** Out ) int nOutputs, cmsToneCurve** Out )
{ {
int i; int i;
Prelin16Data* p16 = (Prelin16Data*) _cmsMallocZero(ContextID, sizeof(Prelin16Data)); Prelin16Data* p16 = _cmsMallocZero(ContextID, sizeof(Prelin16Data));
if (p16 == NULL) return NULL; if (p16 == NULL) return NULL;
p16 ->nInputs = nInputs; p16 ->nInputs = nInputs;
@ -294,7 +293,6 @@ Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16;
p16 -> StageDEF = _cmsCalloc(ContextID, p16 ->nOutputs, sizeof(cmsUInt16Number));
p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16));
p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* ));
@ -379,46 +377,58 @@ cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
int i, index; int i, index;
if (CLUT -> Type != cmsSigCLutElemType) { 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; 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) { 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) || if (((px - x0) != 0) ||
((py - y0) != 0) || ((py - y0) != 0) ||
((pz - z0) != 0) || ((pz - z0) != 0) ||
((pw - w0) != 0)) return FALSE; // Not on exact node ((pw - w0) != 0)) return FALSE; // Not on exact node
index = p16 -> opta[3] * x0 + index = p16 -> opta[3] * x0 +
p16 -> opta[2] * y0 + p16 -> opta[2] * y0 +
p16 -> opta[1] * z0 + p16 -> opta[1] * z0 +
p16 -> opta[0] * w0; p16 -> opta[0] * w0;
} }
else else
if (nChannelsIn == 3) { 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) || if (((px - x0) != 0) ||
((py - y0) != 0) || ((py - y0) != 0) ||
((pz - z0) != 0)) return FALSE; // Not on exact node ((pz - z0) != 0)) return FALSE; // Not on exact node
index = p16 -> opta[2] * x0 + index = p16 -> opta[2] * x0 +
p16 -> opta[1] * y0 + p16 -> opta[1] * y0 +
p16 -> opta[0] * z0; p16 -> opta[0] * z0;
} }
else else
if (nChannelsIn == 1) { 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 if (((px - x0) != 0)) return FALSE; // Not on exact node
index = p16 -> opta[0] * x0; index = p16 -> opta[0] * x0;
@ -434,13 +444,15 @@ cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
return TRUE; return TRUE;
} }
// Auxiliar, to see if two values are equal. // Auxiliar, to see if two values are equal or very different
static static
cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] )
{ {
int i; int i;
for (i=0; i < n; 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; if (White1[i] != White2[i]) return FALSE;
} }
return TRUE; return TRUE;
@ -463,6 +475,8 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor
&WhitePointOut, NULL, &nOuts)) return FALSE; &WhitePointOut, NULL, &nOuts)) return FALSE;
// It needs to be fixed? // It needs to be fixed?
if (Lut ->InputChannels != nIns) return FALSE;
if (Lut ->OutputChannels != nOuts) return FALSE;
cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut);
@ -498,8 +512,14 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor
for (i=0; i < nOuts; i++) { for (i=0; i < nOuts; i++) {
cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]);
WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); if (InversePostLin == NULL) {
cmsFreeToneCurve(InversePostLin); WhiteOut[i] = WhitePointOut[i];
} else {
WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]);
cmsFreeToneCurve(InversePostLin);
}
} }
} }
else { else {
@ -525,8 +545,9 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor
static static
cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
{ {
cmsPipeline* Src; cmsPipeline* Src = NULL;
cmsPipeline* Dest; cmsPipeline* Dest = NULL;
cmsStage* mpe;
cmsStage* CLUT; cmsStage* CLUT;
cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL;
int nGridPoints; int nGridPoints;
@ -538,7 +559,6 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
cmsToneCurve** DataSetOut; cmsToneCurve** DataSetOut;
Prelin16Data* p16; Prelin16Data* p16;
// This is a loosy optimization! does not apply in floating-point cases // This is a loosy optimization! does not apply in floating-point cases
if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
@ -552,6 +572,13 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
Src = *Lut; 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 // Allocate an empty LUT
Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
if (!Dest) return FALSE; if (!Dest) return FALSE;
@ -570,7 +597,8 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
// All seems ok, proceed. // All seems ok, proceed.
NewPreLin = cmsStageDup(PreLin); NewPreLin = cmsStageDup(PreLin);
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin); if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin))
goto Error;
// Remove prelinearization. Since we have duplicated the curve // Remove prelinearization. Since we have duplicated the curve
// in destination LUT, the sampling shoud be applied after this stage. // 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; if (CLUT == NULL) return FALSE;
// Add the CLUT to the destination LUT // 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 // Postlinearization tables are kept unless indicated by flags
if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) {
@ -600,7 +630,8 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
// All seems ok, proceed. // All seems ok, proceed.
NewPostLin = cmsStageDup(PostLin); 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. // In destination LUT, the sampling shoud be applied after this stage.
cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); 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 // 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. // The source LUT whithout pre/post curves is passed as parameter.
if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) {
Error:
// Ops, something went wrong, Restore stages // Ops, something went wrong, Restore stages
if (KeepPreLin != NULL) cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin); if (KeepPreLin != NULL) {
if (KeepPostLin != NULL) cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin); 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); cmsPipelineFree(Dest);
return FALSE; return FALSE;
} }
@ -641,12 +680,11 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
else { else {
p16 = PrelinOpt16alloc(Dest ->ContextID, p16 = PrelinOpt16alloc(Dest ->ContextID,
DataCLUT ->Params, DataCLUT ->Params,
Dest ->InputChannels, Dest ->InputChannels,
DataSetIn, DataSetIn,
Dest ->OutputChannels, Dest ->OutputChannels,
DataSetOut); DataSetOut);
_cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
} }
@ -789,8 +827,8 @@ void PrelinEval8(register const cmsUInt16Number Input[],
cmsUInt8Number r, g, b; cmsUInt8Number r, g, b;
cmsS15Fixed16Number rx, ry, rz; cmsS15Fixed16Number rx, ry, rz;
cmsS15Fixed16Number c0, c1, c2, c3, Rest; cmsS15Fixed16Number c0, c1, c2, c3, Rest;
int OutChan; int OutChan;
register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
Prelin8Data* p8 = (Prelin8Data*) D; Prelin8Data* p8 = (Prelin8Data*) D;
register const cmsInterpParams* p = p8 ->p; register const cmsInterpParams* p = p8 ->p;
int TotalOut = p -> nOutputs; int TotalOut = p -> nOutputs;
@ -864,15 +902,35 @@ void PrelinEval8(register const cmsUInt16Number Input[],
} }
Rest = c1 * rx + c2 * ry + c3 * rz; Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
Output[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest>>16))>>16);
Output[OutChan] = (cmsUInt16Number)c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest));
} }
} }
#undef DENS #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 // We need xput over here
@ -889,6 +947,7 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
cmsStage* OptimizedCLUTmpe; cmsStage* OptimizedCLUTmpe;
cmsColorSpaceSignature ColorSpace, OutputColorSpace; cmsColorSpaceSignature ColorSpace, OutputColorSpace;
cmsStage* OptimizedPrelinMpe; cmsStage* OptimizedPrelinMpe;
cmsStage* mpe;
cmsToneCurve** OptimizedPrelinCurves; cmsToneCurve** OptimizedPrelinCurves;
_cmsStageCLutData* OptimizedPrelinCLUT; _cmsStageCLutData* OptimizedPrelinCLUT;
@ -907,6 +966,14 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
} }
OriginalLut = *Lut; 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)); ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat));
OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat));
nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
@ -953,6 +1020,9 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
// Exclude if non-monotonic // Exclude if non-monotonic
if (!cmsIsToneCurveMonotonic(Trans[t])) if (!cmsIsToneCurveMonotonic(Trans[t]))
lIsSuitable = FALSE; lIsSuitable = FALSE;
if (IsDegenerated(Trans[t]))
lIsSuitable = FALSE;
} }
// If it is not suitable, just quit // If it is not suitable, just quit
@ -968,7 +1038,8 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
LutPlusCurves = cmsPipelineDup(OriginalLut); LutPlusCurves = cmsPipelineDup(OriginalLut);
if (LutPlusCurves == NULL) goto Error; 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 // Create the result LUT
OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); 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); OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans);
// Create and insert the curves at the beginning // 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 // Allocate the CLUT for result
OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL);
// Add the CLUT to the destination LUT // Add the CLUT to the destination LUT
cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe); if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe))
goto Error;
// Resample the LUT // Resample the LUT
if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; 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)); 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) { if (nElements == 256) {
for (j=0; j < nElements; j++) { 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 // Maybe the curves are linear at the end
if (!AllCurvesAreLinear(ObtainedCurves)) { 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 the curves are to be applied in 8 bits, we can save memory
if (_cmsFormatterIs8bit(*InputFormat)) { if (_cmsFormatterIs8bit(*InputFormat)) {
@ -1245,6 +1329,7 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
_cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data; _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data;
Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves);
if (c16 == NULL) goto Error;
*dwFlags |= cmsFLAGS_NOCACHE; *dwFlags |= cmsFLAGS_NOCACHE;
_cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup);
@ -1254,6 +1339,7 @@ cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUI
_cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves);
Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves);
if (c16 == NULL) goto Error;
*dwFlags |= cmsFLAGS_NOCACHE; *dwFlags |= cmsFLAGS_NOCACHE;
_cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); _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 // LUT optimizes to nothing. Set the identity LUT
cmsStageFree(ObtainedCurves); 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; *dwFlags |= cmsFLAGS_NOCACHE;
_cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); _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 // 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 // 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! // 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); cmsUInt8Number b = FROM_16_TO_8(w);
Table[i] = FROM_8_TO_16(b); 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; if (!Dest) return FALSE;
// Assamble the new LUT // Assamble the new LUT
cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1)); if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1)))
goto Error;
if (!IdentityMat) if (!IdentityMat)
cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset)); if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset)))
cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2)); 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 identity on matrix, we can further optimize the curves, so call the join curves routine
if (IdentityMat) { if (IdentityMat) {
@ -1518,6 +1609,10 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
cmsPipelineFree(Src); cmsPipelineFree(Src);
*Lut = Dest; *Lut = Dest;
return TRUE; return TRUE;
Error:
// Leave Src unchanged
cmsPipelineFree(Dest);
return FALSE;
} }
@ -1544,44 +1639,102 @@ static _cmsOptimizationCollection DefaultOptimization[] = {
}; };
// The linked list head // 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 // Register new ways to optimize
cmsBool _cmsRegisterOptimizationPlugin(cmsPluginBase* Data) cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data)
{ {
cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data;
_cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
_cmsOptimizationCollection* fl; _cmsOptimizationCollection* fl;
if (Data == NULL) { if (Data == NULL) {
OptimizationCollection = DefaultOptimization; ctx->OptimizationCollection = NULL;
return TRUE; return TRUE;
} }
// Optimizer callback is required // Optimizer callback is required
if (Plugin ->OptimizePtr == NULL) return FALSE; if (Plugin ->OptimizePtr == NULL) return FALSE;
fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(sizeof(_cmsOptimizationCollection)); fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection));
if (fl == NULL) return FALSE; if (fl == NULL) return FALSE;
// Copy the parameters // Copy the parameters
fl ->OptimizePtr = Plugin ->OptimizePtr; fl ->OptimizePtr = Plugin ->OptimizePtr;
// Keep linked list // Keep linked list
fl ->Next = OptimizationCollection; fl ->Next = ctx->OptimizationCollection;
OptimizationCollection = fl;
// Set the head
ctx ->OptimizationCollection = fl;
// All is ok // All is ok
return TRUE; return TRUE;
} }
// The entry point for LUT optimization // The entry point for LUT optimization
cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut, cmsBool _cmsOptimizePipeline(cmsContext ContextID,
cmsPipeline** PtrLut,
int Intent, int Intent,
cmsUInt32Number* InputFormat, cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat, cmsUInt32Number* OutputFormat,
cmsUInt32Number* dwFlags) cmsUInt32Number* dwFlags)
{ {
_cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
_cmsOptimizationCollection* Opts; _cmsOptimizationCollection* Opts;
cmsBool AnySuccess = FALSE; cmsBool AnySuccess = FALSE;
@ -1611,8 +1764,8 @@ cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut,
if (*dwFlags & cmsFLAGS_NOOPTIMIZE) if (*dwFlags & cmsFLAGS_NOOPTIMIZE)
return FALSE; return FALSE;
// Try built-in optimizations and plug-in // Try plug-in optimizations
for (Opts = OptimizationCollection; for (Opts = ctx->OptimizationCollection;
Opts != NULL; Opts != NULL;
Opts = Opts ->Next) { 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 // Only simple optimizations succeeded
return AnySuccess; 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 static
cmsFloat64Number L2float2(cmsUInt16Number v) cmsFloat64Number L2float2(cmsUInt16Number v)
{ {
return (cmsFloat64Number) v / 652.800; return (cmsFloat64Number) v / 652.800;
} }
// the a/b part // the a/b part
static static
cmsFloat64Number ab2float2(cmsUInt16Number v) cmsFloat64Number ab2float2(cmsUInt16Number v)
{ {
return ((cmsFloat64Number) v / 256.0) - 128.0; return ((cmsFloat64Number) v / 256.0) - 128.0;
} }
static static
cmsUInt16Number L2Fix2(cmsFloat64Number L) cmsUInt16Number L2Fix2(cmsFloat64Number L)
{ {
return _cmsQuickSaturateWord(L * 652.8); return _cmsQuickSaturateWord(L * 652.8);
} }
static static
cmsUInt16Number ab2Fix2(cmsFloat64Number ab) 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) { switch (ColorSpace) {
case cmsSigMCH1Data:
case cmsSig1colorData:
case cmsSigGrayData: return 1; case cmsSigGrayData: return 1;
case cmsSigMCH2Data:
case cmsSig2colorData: return 2; case cmsSig2colorData: return 2;
case cmsSigXYZData: case cmsSigXYZData:
@ -882,10 +885,12 @@ cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
case cmsSigHsvData: case cmsSigHsvData:
case cmsSigHlsData: case cmsSigHlsData:
case cmsSigCmyData: case cmsSigCmyData:
case cmsSigMCH3Data:
case cmsSig3colorData: return 3; case cmsSig3colorData: return 3;
case cmsSigLuvKData: case cmsSigLuvKData:
case cmsSigCmykData: case cmsSigCmykData:
case cmsSigMCH4Data:
case cmsSig4colorData: return 4; case cmsSig4colorData: return 4;
case cmsSigMCH5Data: case cmsSigMCH5Data:

View File

@ -76,12 +76,12 @@ cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord)
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 8 7 6 5 4 3 2 1 // 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 #ifndef CMS_USE_BIG_ENDIAN
cmsUInt8Number* pIn = (cmsUInt8Number*) &QWord; cmsUInt8Number* pIn = (cmsUInt8Number*) QWord;
cmsUInt8Number* pOut = (cmsUInt8Number*) Result; cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
_cmsAssert(Result != NULL); _cmsAssert(Result != NULL);
@ -96,10 +96,14 @@ void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number Q
pOut[0] = pIn[7]; pOut[0] = pIn[7];
#else #else
_cmsAssert(Result != NULL); _cmsAssert(Result != NULL);
*Result = QWord; # ifdef CMS_DONT_USE_INT64
(*Result)[0] = QWord[0];
(*Result)[1] = QWord[1];
# else
*Result = *QWord;
# endif
#endif #endif
} }
@ -189,7 +193,7 @@ cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1) if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
return FALSE; return FALSE;
if (n != NULL) _cmsAdjustEndianess64(n, tmp); if (n != NULL) _cmsAdjustEndianess64(n, &tmp);
return TRUE; return TRUE;
} }
@ -311,7 +315,7 @@ cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
return TRUE; return TRUE;
} }
cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number n) cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
{ {
cmsUInt64Number tmp; cmsUInt64Number tmp;
@ -511,20 +515,34 @@ cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
// Plugin memory management ------------------------------------------------------------------------------------------------- // Plugin memory management -------------------------------------------------------------------------------------------------
static _cmsSubAllocator* PluginPool = NULL;
// Specialized malloc for plug-ins, that is freed upon exit. // Specialized malloc for plug-ins, that is freed upon exit.
void* _cmsPluginMalloc(cmsUInt32Number size) void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
{ {
if (PluginPool == NULL) struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
PluginPool = _cmsCreateSubAlloc(0, 4*1024);
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 // Main plug-in dispatcher
cmsBool CMSEXPORT cmsPlugin(void* Plug_in) cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
{
return cmsPluginTHR(NULL, Plug_in);
}
cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
{ {
cmsPluginBase* Plugin; cmsPluginBase* Plugin;
@ -533,56 +551,64 @@ cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
Plugin = Plugin -> Next) { Plugin = Plugin -> Next) {
if (Plugin -> Magic != cmsPluginMagicNumber) { if (Plugin -> Magic != cmsPluginMagicNumber) {
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
return FALSE; return FALSE;
} }
if (Plugin ->ExpectedVersion > LCMS_VERSION) { if (Plugin ->ExpectedVersion > LCMS_VERSION) {
cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
Plugin ->ExpectedVersion, LCMS_VERSION); Plugin ->ExpectedVersion, LCMS_VERSION);
return FALSE; return FALSE;
} }
switch (Plugin -> Type) { switch (Plugin -> Type) {
case cmsPluginMemHandlerSig: case cmsPluginMemHandlerSig:
if (!_cmsRegisterMemHandlerPlugin(Plugin)) return FALSE; if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginInterpolationSig: case cmsPluginInterpolationSig:
if (!_cmsRegisterInterpPlugin(Plugin)) return FALSE; if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginTagTypeSig: case cmsPluginTagTypeSig:
if (!_cmsRegisterTagTypePlugin(Plugin)) return FALSE; if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginTagSig: case cmsPluginTagSig:
if (!_cmsRegisterTagPlugin(Plugin)) return FALSE; if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginFormattersSig: case cmsPluginFormattersSig:
if (!_cmsRegisterFormattersPlugin(Plugin)) return FALSE; if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginRenderingIntentSig: case cmsPluginRenderingIntentSig:
if (!_cmsRegisterRenderingIntentPlugin(Plugin)) return FALSE; if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginParametricCurveSig: case cmsPluginParametricCurveSig:
if (!_cmsRegisterParametricCurvesPlugin(Plugin)) return FALSE; if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginMultiProcessElementSig: case cmsPluginMultiProcessElementSig:
if (!_cmsRegisterMultiProcessElementPlugin(Plugin)) return FALSE; if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
break; break;
case cmsPluginOptimizationSig: 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; break;
default: 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; return FALSE;
} }
} }
@ -595,18 +621,337 @@ cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
// Revert all plug-ins to default // Revert all plug-ins to default
void CMSEXPORT cmsUnregisterPlugins(void) void CMSEXPORT cmsUnregisterPlugins(void)
{ {
_cmsRegisterMemHandlerPlugin(NULL); cmsUnregisterPluginsTHR(NULL);
_cmsRegisterInterpPlugin(NULL);
_cmsRegisterTagTypePlugin(NULL);
_cmsRegisterTagPlugin(NULL);
_cmsRegisterFormattersPlugin(NULL);
_cmsRegisterRenderingIntentPlugin(NULL);
_cmsRegisterParametricCurvesPlugin(NULL);
_cmsRegisterMultiProcessElementPlugin(NULL);
_cmsRegisterOptimizationPlugin(NULL);
if (PluginPool != NULL)
_cmsSubAllocDestroy(PluginPool);
PluginPool = 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -266,20 +266,20 @@
// This struct holds the memory block currently being write // This struct holds the memory block currently being write
typedef struct { typedef struct {
_cmsStageCLutData* Pipeline; _cmsStageCLutData* Pipeline;
cmsIOHANDLER* m; cmsIOHANDLER* m;
int FirstComponent; int FirstComponent;
int SecondComponent; int SecondComponent;
const char* PreMaj; const char* PreMaj;
const char* PostMaj; const char* PostMaj;
const char* PreMin; const char* PreMin;
const char* PostMin; 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; } cmsPsSamplerCargo;
@ -300,9 +300,9 @@ cmsUInt8Number Word2Byte(cmsUInt16Number w)
static static
cmsUInt8Number L2Byte(cmsUInt16Number w) 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); return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF);
} }
@ -313,14 +313,14 @@ cmsUInt8Number L2Byte(cmsUInt16Number w)
static static
void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b) void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
{ {
_cmsIOPrintf(m, "%02x", b); _cmsIOPrintf(m, "%02x", b);
_cmsPSActualColumn += 2; _cmsPSActualColumn += 2;
if (_cmsPSActualColumn > MAXPSCOLS) { if (_cmsPSActualColumn > MAXPSCOLS) {
_cmsIOPrintf(m, "\n"); _cmsIOPrintf(m, "\n");
_cmsPSActualColumn = 0; _cmsPSActualColumn = 0;
} }
} }
// ----------------------------------------------------------------- PostScript generation // ----------------------------------------------------------------- PostScript generation
@ -346,19 +346,19 @@ static
void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile) void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)
{ {
time_t timer; time_t timer;
cmsMLU *Description, *Copyright; cmsMLU *Description, *Copyright;
char DescASCII[256], CopyrightASCII[256]; char DescASCII[256], CopyrightASCII[256];
time(&timer); time(&timer);
Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag); Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag); Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
DescASCII[0] = DescASCII[255] = 0; DescASCII[0] = DescASCII[255] = 0;
CopyrightASCII[0] = CopyrightASCII[255] = 0; CopyrightASCII[0] = CopyrightASCII[255] = 0;
if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255); if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255);
if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255); if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255);
_cmsIOPrintf(m, "%%!PS-Adobe-3.0\n"); _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");
_cmsIOPrintf(m, "%%\n"); _cmsIOPrintf(m, "%%\n");
@ -451,7 +451,7 @@ void EmitLab2XYZ(cmsIOHANDLER* m)
_cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n"); _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");
_cmsIOPrintf(m, "]\n"); _cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\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, "/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 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"); _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; cmsUInt32Number i;
cmsFloat64Number gamma; cmsFloat64Number gamma;
if (Table == NULL) return; // Error
if (Table ->nEntries <= 0) return; // Empty table if (Table ->nEntries <= 0) return; // Empty table
@ -476,7 +477,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
if (cmsIsToneCurveLinear(Table)) return; if (cmsIsToneCurveLinear(Table)) return;
// Check if is really an exponential. If so, emit "exp" // Check if is really an exponential. If so, emit "exp"
gamma = cmsEstimateGamma(Table, 0.001); gamma = cmsEstimateGamma(Table, 0.001);
if (gamma > 0) { if (gamma > 0) {
_cmsIOPrintf(m, "{ %g exp } bind ", gamma); _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
return; return;
@ -495,7 +496,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
_cmsIOPrintf(m, " ["); _cmsIOPrintf(m, " [");
for (i=0; i < Table->nEntries; i++) { for (i=0; i < Table->nEntries; i++) {
_cmsIOPrintf(m, "%d ", Table->Table16[i]); _cmsIOPrintf(m, "%d ", Table->Table16[i]);
} }
_cmsIOPrintf(m, "] "); // v tab _cmsIOPrintf(m, "] "); // v tab
@ -548,12 +549,14 @@ void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[])
for( i=0; i < n; i++ ) 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 "); _cmsIOPrintf(m, "dup ");
} }
else { 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]; 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 cmsUInt8Number wByteOut; // Value as byte
// We always deal with Lab4 // We always deal with Lab4
wByteOut = Word2Byte(wWordOut); wByteOut = Word2Byte(wWordOut);
WriteByte(sc -> m, wByteOut); WriteByte(sc -> m, wByteOut);
} }
return 1; return 1;
} }
// Writes a Pipeline on memstream. Could be 8 or 16 bits based // 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.FirstComponent = -1;
sc.SecondComponent = -1; sc.SecondComponent = -1;
sc.Pipeline = (_cmsStageCLutData *) mpe ->Data; sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
sc.m = m; sc.m = m;
sc.PreMaj = PreMaj; sc.PreMaj = PreMaj;
sc.PostMaj= PostMaj; sc.PostMaj= PostMaj;
@ -682,8 +685,8 @@ void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
_cmsIOPrintf(m, "["); _cmsIOPrintf(m, "[");
for (i=0; i < sc.Pipeline->Params->nInputs; i++) for (i=0; i < sc.Pipeline->Params->nInputs; i++)
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
_cmsIOPrintf(m, " [\n"); _cmsIOPrintf(m, " [\n");
@ -702,25 +705,25 @@ static
int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint) int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
{ {
_cmsIOPrintf(m, "[ /CIEBasedA\n"); _cmsIOPrintf(m, "[ /CIEBasedA\n");
_cmsIOPrintf(m, " <<\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, "/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, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
EmitWhiteBlackD50(m, BlackPoint); EmitWhiteBlackD50(m, BlackPoint);
EmitIntent(m, INTENT_PERCEPTUAL); 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 static
int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint) int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
{ {
int i; int i;
_cmsIOPrintf(m, "[ /CIEBasedABC\n"); _cmsIOPrintf(m, "[ /CIEBasedABC\n");
_cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "/DecodeABC [ "); _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], _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
Matrix[i + 3*1], Matrix[i + 3*1],
Matrix[i + 3*2]); 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); EmitWhiteBlackD50(m, BlackPoint);
EmitIntent(m, INTENT_PERCEPTUAL); 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* PreMaj;
const char* PostMaj; const char* PostMaj;
const char* PreMin, *PostMin; const char* PreMin, *PostMin;
cmsStage* mpe; cmsStage* mpe;
mpe = Pipeline ->Elements; mpe = Pipeline ->Elements;
switch (cmsStageInputChannels(mpe)) {
switch (cmsStageInputChannels(mpe)) {
case 3: case 3:
_cmsIOPrintf(m, "[ /CIEBasedDEF\n"); _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
@ -797,18 +799,16 @@ int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXY
_cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "<<\n");
if (cmsStageType(mpe) == cmsSigCurveSetElemType) { if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
_cmsIOPrintf(m, "/DecodeDEF [ "); _cmsIOPrintf(m, "/DecodeDEF [ ");
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe)); EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
_cmsIOPrintf(m, "]\n"); _cmsIOPrintf(m, "]\n");
mpe = mpe ->Next; mpe = mpe ->Next;
} }
if (cmsStageType(mpe) == cmsSigCLutElemType) {
if (cmsStageType(mpe) == cmsSigCLutElemType) {
_cmsIOPrintf(m, "/Table "); _cmsIOPrintf(m, "/Table ");
WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0); 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");
_cmsIOPrintf(m, "]\n"); _cmsIOPrintf(m, "]\n");
return 1; return 1;
} }
// Generates a curve from a gray profile // Generates a curve from a gray profile
static static
cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent) cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent)
{ {
cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
cmsHPROFILE hXYZ = cmsCreateXYZProfile(); cmsHPROFILE hXYZ = cmsCreateXYZProfile();
cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE); cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
int i; int i;
for (i=0; i < 256; i++) { if (Out != NULL) {
for (i=0; i < 256; i++) {
cmsUInt8Number Gray = (cmsUInt8Number) i; cmsUInt8Number Gray = (cmsUInt8Number) i;
cmsCIEXYZ XYZ; 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); cmsDeleteTransform(xform);
@ -874,29 +875,29 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Nu
nChannels = T_CHANNELS(InputFormat); 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); hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
Profiles[0] = hProfile; Profiles[0] = hProfile;
Profiles[1] = hLab; Profiles[1] = hLab;
xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0); xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0);
cmsCloseProfile(hLab); cmsCloseProfile(hLab);
if (xform == NULL) { if (xform == NULL) {
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
return 0; return 0;
} }
// Only 1, 3 and 4 channels are allowed // Only 1, 3 and 4 channels are allowed
switch (nChannels) { switch (nChannels) {
case 1: { case 1: {
cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
cmsFreeToneCurve(Gray2Y); cmsFreeToneCurve(Gray2Y);
} }
@ -904,24 +905,25 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Nu
case 3: case 3:
case 4: { case 4: {
cmsUInt32Number OutFrm = TYPE_Lab_16; cmsUInt32Number OutFrm = TYPE_Lab_16;
cmsPipeline* DeviceLink; cmsPipeline* DeviceLink;
_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
DeviceLink = cmsPipelineDup(v ->Lut); DeviceLink = cmsPipelineDup(v ->Lut);
if (DeviceLink == NULL) return 0; if (DeviceLink == NULL) return 0;
dwFlags |= cmsFLAGS_FORCE_CLUT; dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
cmsPipelineFree(DeviceLink); cmsPipelineFree(DeviceLink);
if (rc == 0) return 0;
} }
break; break;
default: 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; return 0;
} }
@ -955,8 +957,8 @@ int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matr
if (ColorSpace == cmsSigGrayData) { if (ColorSpace == cmsSigGrayData) {
cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper); cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50); rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
} }
else else
@ -972,16 +974,16 @@ int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matr
Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ; Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat, rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat,
_cmsStageGetPtrToCurveSet(Shaper), _cmsStageGetPtrToCurveSet(Shaper),
&BlackPointAdaptedToD50); &BlackPointAdaptedToD50);
} }
else { 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 0;
} }
return rc; return rc;
} }
@ -998,11 +1000,11 @@ int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent)
char ColorName[32]; char ColorName[32];
cmsNAMEDCOLORLIST* NamedColorList; 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); xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
if (xform == NULL) return 0; if (xform == NULL) return 0;
NamedColorList = cmsGetNamedColorList(xform); NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0; if (NamedColorList == NULL) return 0;
_cmsIOPrintf(m, "<<\n"); _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 // Does create a Color Space Array on XYZ colorspace for PostScript usage
static static
cmsUInt32Number GenerateCSA(cmsContext ContextID, cmsUInt32Number GenerateCSA(cmsContext ContextID,
cmsHPROFILE hProfile, cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number Intent,
cmsUInt32Number dwFlags, cmsUInt32Number dwFlags,
cmsIOHANDLER* mem) cmsIOHANDLER* mem)
{ {
cmsUInt32Number dwBytesUsed; cmsUInt32Number dwBytesUsed;
cmsPipeline* lut = NULL; cmsPipeline* lut = NULL;
cmsStage* Matrix, *Shaper; cmsStage* Matrix, *Shaper;
// Is a named color profile? // Is a named color profile?
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
} }
else { else {
// Any profile class are allowed (including devicelink), but // Any profile class are allowed (including devicelink), but
// output (PCS) colorspace must be XYZ or Lab // output (PCS) colorspace must be XYZ or Lab
cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
if (ColorSpace != cmsSigXYZData && if (ColorSpace != cmsSigXYZData &&
ColorSpace != cmsSigLabData) { ColorSpace != cmsSigLabData) {
cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
goto Error; goto Error;
} }
// Read the lut with all necessary conversion stages // Read the lut with all necessary conversion stages
lut = _cmsReadInputLUT(hProfile, Intent); lut = _cmsReadInputLUT(hProfile, Intent);
if (lut == NULL) goto Error; if (lut == NULL) goto Error;
// Tone curves + matrix can be implemented without any LUT // Tone curves + matrix can be implemented without any LUT
if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { 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 { else {
// We need a LUT for the rest // We need a LUT for the rest
if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
} }
} }
// Done, keep memory usage // Done, keep memory usage
dwBytesUsed = mem ->UsedSpace; dwBytesUsed = mem ->UsedSpace;
// Get rid of LUT // Get rid of LUT
if (lut != NULL) cmsPipelineFree(lut); if (lut != NULL) cmsPipelineFree(lut);
// Finally, return used byte count // Finally, return used byte count
return dwBytesUsed; return dwBytesUsed;
Error: Error:
if (lut != NULL) cmsPipelineFree(lut); if (lut != NULL) cmsPipelineFree(lut);
return 0; return 0;
} }
// ------------------------------------------------------ Color Rendering Dictionary (CRD) // ------------------------------------------------------ Color Rendering Dictionary (CRD)
@ -1176,15 +1178,15 @@ void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsol
if (lIsAbsolute) { if (lIsAbsolute) {
// For absolute colorimetric intent, encode back to relative // 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,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
_cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\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" "{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" "{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", "{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; return;
} }
@ -1285,50 +1287,50 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32N
cmsCIEXYZ BlackPointAdaptedToD50; cmsCIEXYZ BlackPointAdaptedToD50;
cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
cmsUInt32Number InFrm = TYPE_Lab_16; cmsUInt32Number InFrm = TYPE_Lab_16;
int RelativeEncodingIntent; int RelativeEncodingIntent;
cmsColorSpaceSignature ColorSpace; cmsColorSpaceSignature ColorSpace;
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
if (hLab == NULL) return 0; if (hLab == NULL) return 0;
OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); 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; RelativeEncodingIntent = Intent;
if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
// Use V4 Lab always // Use V4 Lab always
Profiles[0] = hLab; Profiles[0] = hLab;
Profiles[1] = hProfile; Profiles[1] = hProfile;
xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
Profiles, 2, TYPE_Lab_DBL, Profiles, 2, TYPE_Lab_DBL,
OutputFormat, RelativeEncodingIntent, 0); OutputFormat, RelativeEncodingIntent, 0);
cmsCloseProfile(hLab); cmsCloseProfile(hLab);
if (xform == NULL) { 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; return 0;
} }
// Get a copy of the internal devicelink // Get a copy of the internal devicelink
v = (_cmsTRANSFORM*) xform; v = (_cmsTRANSFORM*) xform;
DeviceLink = cmsPipelineDup(v ->Lut); DeviceLink = cmsPipelineDup(v ->Lut);
if (DeviceLink == NULL) return 0; if (DeviceLink == NULL) return 0;
// We need a CLUT // We need a CLUT
dwFlags |= cmsFLAGS_FORCE_CLUT; dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
_cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "/ColorRenderingType 1\n"); _cmsIOPrintf(m, "/ColorRenderingType 1\n");
@ -1413,19 +1415,19 @@ int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cms
cmsUInt32Number OutputFormat; cmsUInt32Number OutputFormat;
char ColorName[32]; char ColorName[32];
char Colorant[128]; char Colorant[128];
cmsNAMEDCOLORLIST* NamedColorList; cmsNAMEDCOLORLIST* NamedColorList;
OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE); OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
nColorant = T_CHANNELS(OutputFormat); nColorant = T_CHANNELS(OutputFormat);
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags); xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
if (xform == NULL) return 0; if (xform == NULL) return 0;
NamedColorList = cmsGetNamedColorList(xform); NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0; if (NamedColorList == NULL) return 0;
_cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile"); _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
@ -1468,128 +1470,128 @@ int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cms
static static
cmsUInt32Number GenerateCRD(cmsContext ContextID, cmsUInt32Number GenerateCRD(cmsContext ContextID,
cmsHPROFILE hProfile, cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number dwFlags, cmsUInt32Number Intent, cmsUInt32Number dwFlags,
cmsIOHANDLER* mem) 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? // Is a named color profile?
if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
return 0; return 0;
} }
} }
else { else {
// CRD are always implemented as LUT // CRD are always implemented as LUT
if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
return 0; return 0;
} }
} }
if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
_cmsIOPrintf(mem, "%%%%EndResource\n"); _cmsIOPrintf(mem, "%%%%EndResource\n");
_cmsIOPrintf(mem, "\n%% CRD End\n"); _cmsIOPrintf(mem, "\n%% CRD End\n");
} }
// Done, keep memory usage // Done, keep memory usage
dwBytesUsed = mem ->UsedSpace; dwBytesUsed = mem ->UsedSpace;
// Finally, return used byte count // Finally, return used byte count
return dwBytesUsed; return dwBytesUsed;
cmsUNUSED_PARAMETER(ContextID); cmsUNUSED_PARAMETER(ContextID);
} }
cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
cmsPSResourceType Type, cmsPSResourceType Type,
cmsHPROFILE hProfile, cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number Intent,
cmsUInt32Number dwFlags, cmsUInt32Number dwFlags,
cmsIOHANDLER* io) cmsIOHANDLER* io)
{ {
cmsUInt32Number rc; cmsUInt32Number rc;
switch (Type) { switch (Type) {
case cmsPS_RESOURCE_CSA: case cmsPS_RESOURCE_CSA:
rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io); rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
break; break;
default: default:
case cmsPS_RESOURCE_CRD: case cmsPS_RESOURCE_CRD:
rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io); rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
break; break;
} }
return rc; return rc;
} }
cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
cmsHPROFILE hProfile, cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number dwFlags, cmsUInt32Number Intent, cmsUInt32Number dwFlags,
void* Buffer, cmsUInt32Number dwBufferLen) void* Buffer, cmsUInt32Number dwBufferLen)
{ {
cmsIOHANDLER* mem; cmsIOHANDLER* mem;
cmsUInt32Number dwBytesUsed; cmsUInt32Number dwBytesUsed;
// Set up the serialization engine // Set up the serialization engine
if (Buffer == NULL) if (Buffer == NULL)
mem = cmsOpenIOhandlerFromNULL(ContextID); mem = cmsOpenIOhandlerFromNULL(ContextID);
else else
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
if (!mem) return 0; 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 // 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 // Does create a Color Space Array on XYZ colorspace for PostScript usage
cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
cmsHPROFILE hProfile, cmsHPROFILE hProfile,
cmsUInt32Number Intent, cmsUInt32Number Intent,
cmsUInt32Number dwFlags, cmsUInt32Number dwFlags,
void* Buffer, void* Buffer,
cmsUInt32Number dwBufferLen) cmsUInt32Number dwBufferLen)
{ {
cmsIOHANDLER* mem; cmsIOHANDLER* mem;
cmsUInt32Number dwBytesUsed; cmsUInt32Number dwBytesUsed;
if (Buffer == NULL) if (Buffer == NULL)
mem = cmsOpenIOhandlerFromNULL(ContextID); mem = cmsOpenIOhandlerFromNULL(ContextID);
else else
mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
if (!mem) return 0; 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 // Get rid of memory stream
cmsCloseIOhandler(mem); cmsCloseIOhandler(mem);
return dwBytesUsed; return dwBytesUsed;
} }

View File

@ -27,6 +27,8 @@
#include "lcms2_internal.h" #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 // This file contains routines for resampling and LUT optimization, black point detection
// and black preservation. // and black preservation.
@ -38,13 +40,13 @@
static static
cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent) cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
{ {
cmsHPROFILE hLab = cmsCreateLab4Profile(NULL); cmsContext ContextID = cmsGetProfileContextID(hProfile);
cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
cmsHTRANSFORM xform; cmsHTRANSFORM xform;
cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE }; cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE };
cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 }; cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 };
cmsHPROFILE hProfiles[4]; cmsHPROFILE hProfiles[4];
cmsUInt32Number Intents[4]; cmsUInt32Number Intents[4];
cmsContext ContextID = cmsGetProfileContextID(hProfile);
hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab; 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; 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); cmsCloseProfile(hLab);
if (xform == NULL) { 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; BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
return FALSE; return FALSE;
} }
@ -144,7 +146,6 @@ cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab // Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
static static
cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile) cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile)
{ {
cmsHTRANSFORM hRoundTrip; cmsHTRANSFORM hRoundTrip;
cmsCIELab LabIn, LabOut; 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 // 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 // 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. // involves to turn BP to neutral and to use only L component.
cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
{ {
cmsProfileClassSignature devClass;
// Zero for black point // Make sure the device class is adequate
if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { 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; // Make sure intent is adequate
return FALSE; 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 // 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. // well specified enough to use it. Black point tag is deprecated in V4.
if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
(Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { (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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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) ); *alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) );
*theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) ); *theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) );
if (*alpha >= SECTORS) if (*alpha >= SECTORS)
*alpha = SECTORS-1; *alpha = SECTORS-1;
if (*theta >= SECTORS) if (*theta >= SECTORS)
*theta = SECTORS-1; *theta = SECTORS-1;
} }
@ -326,8 +326,8 @@ cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp)
// Housekeeping // Housekeeping
_cmsAssert(gbd != NULL); _cmsAssert(gbd != NULL);
_cmsAssert(Lab != NULL); _cmsAssert(Lab != NULL);
_cmsAssert(sp != NULL); _cmsAssert(sp != NULL);
// Center L* by substracting half of its domain, that's 50 // Center L* by substracting half of its domain, that's 50
_cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b); _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 FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[])
{ {
int nSectors = 0; int nSectors = 0;
int i, a, t; int a, t;
cmsUInt32Number i;
cmsGDBPoint* pt; cmsGDBPoint* pt;
for (i=0; i < NSTEPS; i++) { for (i=0; i < NSTEPS; i++) {
@ -476,7 +477,7 @@ cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta)
cmsVEC3 Centre; cmsVEC3 Centre;
cmsLine ray; cmsLine ray;
int nCloseSectors; int nCloseSectors;
cmsGDBPoint* Close[NSTEPS]; cmsGDBPoint* Close[NSTEPS + 1];
cmsSpherical closel, templ; cmsSpherical closel, templ;
cmsLine edge; cmsLine edge;
int k, m; int k, m;
@ -553,13 +554,13 @@ cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags)
_cmsAssert(hGBD != NULL); _cmsAssert(hGBD != NULL);
// Interpolate black // Interpolate black
for (alpha = 0; alpha <= SECTORS; alpha++) { for (alpha = 0; alpha < SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE;
} }
// Interpolate white // Interpolate white
for (alpha = 0; alpha <= SECTORS; alpha++) { for (alpha = 0; alpha < SECTORS; alpha++) {
if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE;
} }
@ -567,7 +568,7 @@ cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags)
// Interpolate Mid // Interpolate Mid
for (theta = 1; theta < SECTORS; theta++) { 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; if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE;
} }
@ -708,10 +709,10 @@ cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname)
else else
if (pt ->Type == GP_MODELED) if (pt ->Type == GP_MODELED)
fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5); 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); fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0);
} }
if ((j == SECTORS - 1) && (i == SECTORS - 1)) if ((j == SECTORS - 1) && (i == SECTORS - 1))
fprintf (fp, "]\n"); fprintf (fp, "]\n");

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // 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 // Obtains WhitePoint from Temperature
cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK) cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK)
{ {
cmsFloat64Number x, y; cmsFloat64Number x, y;
cmsFloat64Number T, T2, T3; cmsFloat64Number T, T2, T3;
// cmsFloat64Number M1, M2; // cmsFloat64Number M1, M2;
_cmsAssert(WhitePoint != NULL); _cmsAssert(WhitePoint != NULL);
T = TempK; T = TempK;
T2 = T*T; // Square T2 = T*T; // Square
T3 = T2*T; // Cube 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.) if (T >= 4000. && T <= 7000.)
{ {
x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
} }
else else
// or for correlated color temperature (T) between 7000K and 25000K: // or for correlated color temperature (T) between 7000K and 25000K:
if (T > 7000.0 && T <= 25000.0) if (T > 7000.0 && T <= 25000.0)
{ {
x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
} }
else { else {
cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp"); cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp");
return FALSE; 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); // 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); // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
WhitePoint -> x = x; WhitePoint -> x = x;
WhitePoint -> y = y; WhitePoint -> y = y;
WhitePoint -> Y = 1.0; WhitePoint -> Y = 1.0;
return TRUE; return TRUE;
} }
@ -143,46 +143,46 @@ static ISOTEMPERATURE isotempdata[] = {
// Robertson's method // Robertson's method
cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint) cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint)
{ {
int j; cmsUInt32Number j;
cmsFloat64Number us,vs; cmsFloat64Number us,vs;
cmsFloat64Number uj,vj,tj,di,dj,mi,mj; cmsFloat64Number uj,vj,tj,di,dj,mi,mj;
cmsFloat64Number xs, ys; cmsFloat64Number xs, ys;
_cmsAssert(WhitePoint != NULL); _cmsAssert(WhitePoint != NULL);
_cmsAssert(TempK != NULL); _cmsAssert(TempK != NULL);
di = mi = 0; di = mi = 0;
xs = WhitePoint -> x; xs = WhitePoint -> x;
ys = WhitePoint -> y; 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); us = (2*xs) / (-xs + 6*ys + 1.5);
vs = (3*ys) / (-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; uj = isotempdata[j].ut;
vj = isotempdata[j].vt; vj = isotempdata[j].vt;
tj = isotempdata[j].tt; tj = isotempdata[j].tt;
mj = isotempdata[j].mirek; 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 // Found a match
*TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); *TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
return TRUE; return TRUE;
} }
di = dj; di = dj;
mi = mj; mi = mj;
} }
// Not found // Not found
return FALSE; return FALSE;
} }
@ -226,41 +226,41 @@ cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion,
_cmsMAT3per(&Tmp, &Cone, Chad); _cmsMAT3per(&Tmp, &Cone, Chad);
_cmsMAT3per(Conversion, &Chad_Inv, &Tmp); _cmsMAT3per(Conversion, &Chad_Inv, &Tmp);
return TRUE; return TRUE;
} }
// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed // 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) cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll)
{ {
cmsMAT3 LamRigg = {{ // Bradford matrix cmsMAT3 LamRigg = {{ // Bradford matrix
{{ 0.8951, 0.2664, -0.1614 }}, {{ 0.8951, 0.2664, -0.1614 }},
{{ -0.7502, 1.7135, 0.0367 }}, {{ -0.7502, 1.7135, 0.0367 }},
{{ 0.0389, -0.0685, 1.0296 }} {{ 0.0389, -0.0685, 1.0296 }}
}}; }};
if (ConeMatrix == NULL) if (ConeMatrix == NULL)
ConeMatrix = &LamRigg; 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 // Same as anterior, but assuming D50 destination. White point is given in xyY
static static
cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt) cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt)
{ {
cmsCIEXYZ Dn; cmsCIEXYZ Dn;
cmsMAT3 Bradford; cmsMAT3 Bradford;
cmsMAT3 Tmp; 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; Tmp = *r;
_cmsMAT3per(r, &Bradford, &Tmp); _cmsMAT3per(r, &Bradford, &Tmp);
return TRUE; return TRUE;
} }
// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ // 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) cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs)
{ {
cmsVEC3 WhitePoint, Coef; cmsVEC3 WhitePoint, Coef;
cmsMAT3 Result, Primaries; cmsMAT3 Result, Primaries;
cmsFloat64Number xn, yn; cmsFloat64Number xn, yn;
cmsFloat64Number xr, yr; cmsFloat64Number xr, yr;
cmsFloat64Number xg, yg; cmsFloat64Number xg, yg;
cmsFloat64Number xb, yb; cmsFloat64Number xb, yb;
xn = WhitePt -> x; xn = WhitePt -> x;
yn = WhitePt -> y; yn = WhitePt -> y;
xr = Primrs -> Red.x; xr = Primrs -> Red.x;
yr = Primrs -> Red.y; yr = Primrs -> Red.y;
xg = Primrs -> Green.x; xg = Primrs -> Green.x;
yg = Primrs -> Green.y; yg = Primrs -> Green.y;
xb = Primrs -> Blue.x; xb = Primrs -> Blue.x;
yb = Primrs -> Blue.y; yb = Primrs -> Blue.y;
// Build Primaries matrix // Build Primaries matrix
_cmsVEC3init(&Primaries.v[0], xr, xg, xb); _cmsVEC3init(&Primaries.v[0], xr, xg, xb);
_cmsVEC3init(&Primaries.v[1], yr, yg, yb); _cmsVEC3init(&Primaries.v[1], yr, yg, yb);
_cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); _cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb));
// Result = Primaries ^ (-1) inverse matrix // Result = Primaries ^ (-1) inverse matrix
if (!_cmsMAT3inverse(&Primaries, &Result)) if (!_cmsMAT3inverse(&Primaries, &Result))
return FALSE; 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 ... // Across inverse primaries ...
_cmsMAT3eval(&Coef, &Result, &WhitePoint); _cmsMAT3eval(&Coef, &Result, &WhitePoint);
// Give us the Coefs, then I build transformation matrix // 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[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[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)); _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 // Adapts a color to a given illuminant. Original color is expected to have
// a SourceWhitePt white point. // a SourceWhitePt white point.
cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result,
const cmsCIEXYZ* SourceWhitePt, const cmsCIEXYZ* SourceWhitePt,
const cmsCIEXYZ* Illuminant, const cmsCIEXYZ* Illuminant,
const cmsCIEXYZ* Value) const cmsCIEXYZ* Value)
{ {
cmsMAT3 Bradford; cmsMAT3 Bradford;
cmsVEC3 In, Out; cmsVEC3 In, Out;
_cmsAssert(Result != NULL); _cmsAssert(Result != NULL);
_cmsAssert(SourceWhitePt != NULL); _cmsAssert(SourceWhitePt != NULL);
_cmsAssert(Illuminant != NULL); _cmsAssert(Illuminant != NULL);
_cmsAssert(Value != 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); _cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z);
_cmsMAT3eval(&Out, &Bradford, &In); _cmsMAT3eval(&Out, &Bradford, &In);
Result -> X = Out.n[0]; Result -> X = Out.n[0];
Result -> Y = Out.n[1]; Result -> Y = Out.n[1];
Result -> Z = Out.n[2]; Result -> Z = Out.n[2];
return TRUE; return TRUE;
} }

View File

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// //
// Little Color Management System // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -29,44 +29,120 @@
// Transformations stuff // Transformations stuff
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Alarm codes for 16-bit transformations, because the fixed range of containers there are #define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
// no values left to mark out of gamut. volatile is C99 per 6.2.5
static volatile cmsUInt16Number Alarm[cmsMAXCHANNELS]; // The Context0 observer adaptation state.
static volatile cmsFloat64Number GlobalAdaptationState = 0; _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 // 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 CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
{ {
cmsFloat64Number OldVal = GlobalAdaptationState; return cmsSetAdaptationStateTHR(NULL, d);
if (d >= 0)
GlobalAdaptationState = d;
return OldVal;
} }
// 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); _cmsAssert(NewAlarm != NULL);
for (i=0; i < cmsMAXCHANNELS; i++) cmsSetAlarmCodesTHR(NULL, NewAlarm);
Alarm[i] = NewAlarm[i];
} }
// You can get the codes cas well
void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
{ {
int i;
_cmsAssert(OldAlarm != NULL); _cmsAssert(OldAlarm != NULL);
cmsGetAlarmCodesTHR(NULL, OldAlarm);
for (i=0; i < cmsMAXCHANNELS; i++)
OldAlarm[i] = Alarm[i];
} }
// 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 // Get rid of transform resources
void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform) void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
{ {
@ -89,11 +165,13 @@ void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
if (p ->Sequence) if (p ->Sequence)
cmsFreeProfileSequenceDescription(p ->Sequence); cmsFreeProfileSequenceDescription(p ->Sequence);
LCMS_FREE_LOCK(&p->rwlock); if (p ->UserData)
p ->FreeUserData(p ->ContextID, p ->UserData);
_cmsFree(p ->ContextID, (void *) p); _cmsFree(p ->ContextID, (void *) p);
} }
// Apply transform // Apply transform.
void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
const void* InputBuffer, const void* InputBuffer,
void* OutputBuffer, void* OutputBuffer,
@ -102,7 +180,20 @@ void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
{ {
_cmsTRANSFORM* p = (_cmsTRANSFORM*) 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 static
void FloatXFORM(_cmsTRANSFORM* p, void FloatXFORM(_cmsTRANSFORM* p,
const void* in, const void* in,
void* out, cmsUInt32Number Size) void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{ {
cmsUInt8Number* accum; cmsUInt8Number* accum;
cmsUInt8Number* output; cmsUInt8Number* output;
@ -126,7 +217,7 @@ void FloatXFORM(_cmsTRANSFORM* p,
for (i=0; i < Size; i++) { 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? // Any gamut chack to do?
if (p ->GamutCheck != NULL) { if (p ->GamutCheck != NULL) {
@ -154,7 +245,31 @@ void FloatXFORM(_cmsTRANSFORM* p,
} }
// Back to asked representation // 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 static
void NullXFORM(_cmsTRANSFORM* p, void NullXFORM(_cmsTRANSFORM* p,
const void* in, const void* in,
void* out, cmsUInt32Number Size) void* out, cmsUInt32Number Size,
cmsUInt32Number Stride)
{ {
cmsUInt8Number* accum; cmsUInt8Number* accum;
cmsUInt8Number* output; cmsUInt8Number* output;
@ -177,8 +293,8 @@ void NullXFORM(_cmsTRANSFORM* p,
for (i=0; i < n; i++) { for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size); accum = p -> FromInput(p, wIn, accum, Stride);
output = p -> ToOutput(p, wIn, output, Size); output = p -> ToOutput(p, wIn, output, Stride);
} }
} }
@ -187,7 +303,7 @@ void NullXFORM(_cmsTRANSFORM* p,
static static
void PrecalculatedXFORM(_cmsTRANSFORM* p, void PrecalculatedXFORM(_cmsTRANSFORM* p,
const void* in, const void* in,
void* out, cmsUInt32Number Size) void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{ {
register cmsUInt8Number* accum; register cmsUInt8Number* accum;
register cmsUInt8Number* output; register cmsUInt8Number* output;
@ -200,14 +316,14 @@ void PrecalculatedXFORM(_cmsTRANSFORM* p,
for (i=0; i < n; i++) { 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); 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 static
void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
const cmsUInt16Number wIn[], const cmsUInt16Number wIn[],
@ -219,9 +335,12 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
if (wOutOfGamut >= 1) { if (wOutOfGamut >= 1) {
cmsUInt16Number i; cmsUInt16Number i;
_cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
for (i=0; i < p ->Lut->OutputChannels; i++) for (i=0; i < p ->Lut->OutputChannels; i++) {
wOut[i] = Alarm[i];
wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
}
} }
else else
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
@ -231,7 +350,7 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
static static
void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
const void* in, const void* in,
void* out, cmsUInt32Number Size) void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{ {
cmsUInt8Number* accum; cmsUInt8Number* accum;
cmsUInt8Number* output; cmsUInt8Number* output;
@ -244,9 +363,9 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
for (i=0; i < n; i++) { for (i=0; i < n; i++) {
accum = p -> FromInput(p, wIn, accum, Size); accum = p -> FromInput(p, wIn, accum, Stride);
TransformOnePixelWithGamutCheck(p, wIn, wOut); 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 static
void CachedXFORM(_cmsTRANSFORM* p, void CachedXFORM(_cmsTRANSFORM* p,
const void* in, const void* in,
void* out, cmsUInt32Number Size) void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{ {
cmsUInt8Number* accum; cmsUInt8Number* accum;
cmsUInt8Number* output; cmsUInt8Number* output;
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
cmsUInt32Number i, n; cmsUInt32Number i, n;
cmsUInt16Number CacheIn[cmsMAXCHANNELS], CacheOut[cmsMAXCHANNELS]; _cmsCACHE Cache;
accum = (cmsUInt8Number*) in; accum = (cmsUInt8Number*) in;
output = (cmsUInt8Number*) out; output = (cmsUInt8Number*) out;
@ -271,36 +390,28 @@ void CachedXFORM(_cmsTRANSFORM* p,
memset(wIn, 0, sizeof(wIn)); memset(wIn, 0, sizeof(wIn));
memset(wOut, 0, sizeof(wOut)); memset(wOut, 0, sizeof(wOut));
// Get copy of zero cache
LCMS_READ_LOCK(&p ->rwlock); memcpy(&Cache, &p ->Cache, sizeof(Cache));
memmove(CacheIn, p ->CacheIn, sizeof(CacheIn));
memmove(CacheOut, p ->CacheOut, sizeof(CacheOut));
LCMS_UNLOCK(&p ->rwlock);
for (i=0; i < n; i++) { 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 { else {
p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
memmove(CacheIn, wIn, sizeof(CacheIn)); memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
memmove(CacheOut, wOut, sizeof(CacheOut)); 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 static
void CachedXFORMGamutCheck(_cmsTRANSFORM* p, void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
const void* in, const void* in,
void* out, cmsUInt32Number Size) void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
{ {
cmsUInt8Number* accum; cmsUInt8Number* accum;
cmsUInt8Number* output; cmsUInt8Number* output;
cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
cmsUInt32Number i, n; cmsUInt32Number i, n;
cmsUInt16Number CacheIn[cmsMAXCHANNELS], CacheOut[cmsMAXCHANNELS]; _cmsCACHE Cache;
accum = (cmsUInt8Number*) in; accum = (cmsUInt8Number*) in;
output = (cmsUInt8Number*) out; output = (cmsUInt8Number*) out;
@ -324,52 +435,211 @@ void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
LCMS_READ_LOCK(&p ->rwlock); // Get copy of zero cache
memmove(CacheIn, p ->CacheIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); memcpy(&Cache, &p ->Cache, sizeof(Cache));
memmove(CacheOut, p ->CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
LCMS_UNLOCK(&p ->rwlock);
for (i=0; i < n; i++) { 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) { if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
memmove(wOut, CacheOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
} }
else { else {
TransformOnePixelWithGamutCheck(p, wIn, wOut); TransformOnePixelWithGamutCheck(p, wIn, wOut);
memmove(CacheIn, wIn, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
memmove(CacheOut, wOut, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); 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));
}
} }
// Register new ways to transform
// Allocate transform struct and set it to defaults cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
static
_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
{ {
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 // Allocate needed memory
_cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
if (!p) return NULL; 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 // 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. // 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 ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
p ->ToOutputFloat = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
@ -378,21 +648,28 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
return NULL; return NULL;
} }
// Float transforms don't use caché, always are non-NULL if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
p ->xform = FloatXFORM;
p ->xform = NullFloatXFORM;
}
else {
// Float transforms don't use caché, always are non-NULL
p ->xform = FloatXFORM;
}
} }
else { else {
if (InputFormat == 0 && OutputFormat == 0) { if (*InputFormat == 0 && *OutputFormat == 0) {
p ->FromInput = p ->ToOutput = NULL; p ->FromInput = p ->ToOutput = NULL;
*dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
} }
else { else {
int BytesPerPixelInput; int BytesPerPixelInput;
p ->FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
p ->ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
if (p ->FromInput == NULL || p ->ToOutput == NULL) { if (p ->FromInput == NULL || p ->ToOutput == NULL) {
@ -403,25 +680,25 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
BytesPerPixelInput = T_BYTES(p ->InputFormat); BytesPerPixelInput = T_BYTES(p ->InputFormat);
if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) 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; p ->xform = NullXFORM;
} }
else { else {
if (dwFlags & cmsFLAGS_NOCACHE) { if (*dwFlags & cmsFLAGS_NOCACHE) {
if (dwFlags & cmsFLAGS_GAMUTCHECK) if (*dwFlags & cmsFLAGS_GAMUTCHECK)
p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no caché p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no caché
else else
p ->xform = PrecalculatedXFORM; // No caché, no gamut check p ->xform = PrecalculatedXFORM; // No caché, no gamut check
} }
else { else {
if (dwFlags & cmsFLAGS_GAMUTCHECK) if (*dwFlags & cmsFLAGS_GAMUTCHECK)
p ->xform = CachedXFORMGamutCheck; // Gamut check, caché p ->xform = CachedXFORMGamutCheck; // Gamut check, caché
else else
p ->xform = CachedXFORM; // No gamut check, caché p ->xform = CachedXFORM; // No gamut check, caché
@ -430,14 +707,11 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
} }
} }
p ->InputFormat = *InputFormat;
// Create a mutex for shared memory p ->OutputFormat = *OutputFormat;
LCMS_CREATE_LOCK(&p->rwlock); p ->dwOriginalFlags = *dwFlags;
p ->InputFormat = InputFormat;
p ->OutputFormat = OutputFormat;
p ->dwOriginalFlags = dwFlags;
p ->ContextID = ContextID; p ->ContextID = ContextID;
p ->UserData = NULL;
return p; return p;
} }
@ -448,36 +722,49 @@ cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpac
cmsColorSpaceSignature PostColorSpace; cmsColorSpaceSignature PostColorSpace;
int i; int i;
if (hProfiles[0] == NULL) return FALSE; if (nProfiles <= 0) return FALSE;
if (hProfiles[0] == NULL) return FALSE;
*Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
for (i=0; i < nProfiles; i++) { for (i=0; i < nProfiles; i++) {
cmsProfileClassSignature cls;
cmsHPROFILE hProfile = hProfiles[i]; cmsHPROFILE hProfile = hProfiles[i];
int lIsInput = (PostColorSpace != cmsSigXYZData) && int lIsInput = (PostColorSpace != cmsSigXYZData) &&
(PostColorSpace != cmsSigLabData); (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); ColorSpaceIn = cmsGetColorSpace(hProfile);
ColorSpaceOut = cmsGetPCS(hProfile); ColorSpaceOut = cmsGetPCS(hProfile);
} }
else { else
{
ColorSpaceIn = cmsGetPCS(hProfile); ColorSpaceIn = cmsGetPCS(hProfile);
ColorSpaceOut = cmsGetColorSpace(hProfile); ColorSpaceOut = cmsGetColorSpace(hProfile);
} }
if (i==0)
*Input = ColorSpaceIn;
PostColorSpace = ColorSpaceOut; PostColorSpace = ColorSpaceOut;
} }
*Output = PostColorSpace; *Output = PostColorSpace;
return TRUE; return TRUE;
} }
// Check colorspace // 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. // New to lcms 2.0 -- have all parameters available.
cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
@ -511,28 +814,31 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
cmsUInt32Number dwFlags) cmsUInt32Number dwFlags)
{ {
_cmsTRANSFORM* xform; _cmsTRANSFORM* xform;
cmsBool FloatTransform;
cmsColorSpaceSignature EntryColorSpace; cmsColorSpaceSignature EntryColorSpace;
cmsColorSpaceSignature ExitColorSpace; cmsColorSpaceSignature ExitColorSpace;
cmsPipeline* Lut; cmsPipeline* Lut;
cmsUInt32Number LastIntent = Intents[nProfiles-1]; 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 gamut check is requested, make sure we have a gamut profile
if (dwFlags & cmsFLAGS_GAMUTCHECK) { if (dwFlags & cmsFLAGS_GAMUTCHECK) {
if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
} }
// On floating point transforms, inhibit optimizations // On floating point transforms, inhibit cache
FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat));
if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
dwFlags |= cmsFLAGS_NOCACHE; dwFlags |= cmsFLAGS_NOCACHE;
// Mark entry/exit spaces // Mark entry/exit spaces
if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
return NULL; return NULL;
} }
// Check if proper colorspaces // Check if proper colorspaces
if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
@ -552,21 +858,29 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
return NULL; return NULL;
} }
// Optimize the LUT if possible // Check channel count
_cmsOptimizePipeline(&Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); 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 // All seems ok
xform = AllocEmptyTransform(ContextID, InputFormat, OutputFormat, dwFlags); xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
if (xform == NULL) { if (xform == NULL) {
cmsPipelineFree(Lut);
return NULL; return NULL;
} }
// Keep values // Keep values
xform ->EntryColorSpace = EntryColorSpace; xform ->EntryColorSpace = EntryColorSpace;
xform ->ExitColorSpace = ExitColorSpace; 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 // 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 this is a cached transform, init first value, which is zero (16 bits only)
if (!(dwFlags & cmsFLAGS_NOCACHE)) { if (!(dwFlags & cmsFLAGS_NOCACHE)) {
memset(&xform ->CacheIn, 0, sizeof(xform ->CacheIn)); memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
if (xform ->GamutCheck != NULL) { if (xform ->GamutCheck != NULL) {
TransformOnePixelWithGamutCheck(xform, xform ->CacheIn, xform->CacheOut); TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
} }
else { 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. // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
cmsHPROFILE hProfiles[], cmsHPROFILE hProfiles[],
cmsUInt32Number nProfiles, cmsUInt32Number nProfiles,
@ -651,7 +964,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
for (i=0; i < nProfiles; i++) { for (i=0; i < nProfiles; i++) {
BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
Intents[i] = Intent; 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; 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; 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))) if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
@ -772,7 +1085,22 @@ cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
return xform -> ContextID; 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 // For backwards compatibility
cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
@ -782,18 +1110,17 @@ cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
cmsFormatter16 FromInput, ToOutput; cmsFormatter16 FromInput, ToOutput;
cmsUInt32Number BytesPerPixelInput;
// We only can afford to change formatters if previous transform is at least 16 bits // 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)) { 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"); cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
return FALSE; return FALSE;
} }
FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
if (FromInput == NULL || ToOutput == NULL) { if (FromInput == NULL || ToOutput == NULL) {
@ -803,7 +1130,7 @@ cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
xform ->InputFormat = InputFormat; xform ->InputFormat = InputFormat;
xform ->OutputFormat = OutputFormat; xform ->OutputFormat = OutputFormat;
xform ->FromInput = FromInput; xform ->FromInput = FromInput;
xform ->ToOutput = ToOutput; xform ->ToOutput = ToOutput;
return TRUE; return TRUE;
} }

View File

@ -63,9 +63,11 @@ _cmsDefaultICCintents = _cmsDefaultICCintents
cmsDeleteTransform = cmsDeleteTransform cmsDeleteTransform = cmsDeleteTransform
cmsDeltaE = cmsDeltaE cmsDeltaE = cmsDeltaE
cmsDetectBlackPoint = cmsDetectBlackPoint cmsDetectBlackPoint = cmsDetectBlackPoint
cmsDetectDestinationBlackPoint = cmsDetectDestinationBlackPoint
cmsDetectTAC = cmsDetectTAC cmsDetectTAC = cmsDetectTAC
cmsDesaturateLab = cmsDesaturateLab cmsDesaturateLab = cmsDesaturateLab
cmsDoTransform = cmsDoTransform cmsDoTransform = cmsDoTransform
cmsDoTransformStride = cmsDoTransformStride
_cmsDoubleTo15Fixed16 = _cmsDoubleTo15Fixed16 _cmsDoubleTo15Fixed16 = _cmsDoubleTo15Fixed16
_cmsDoubleTo8Fixed8 = _cmsDoubleTo8Fixed8 _cmsDoubleTo8Fixed8 = _cmsDoubleTo8Fixed8
_cmsDupMem = _cmsDupMem _cmsDupMem = _cmsDupMem
@ -74,6 +76,8 @@ cmsDupProfileSequenceDescription = cmsDupProfileSequenceDescription
cmsDupToneCurve = cmsDupToneCurve cmsDupToneCurve = cmsDupToneCurve
_cmsEncodeDateTimeNumber = _cmsEncodeDateTimeNumber _cmsEncodeDateTimeNumber = _cmsEncodeDateTimeNumber
cmsEstimateGamma = cmsEstimateGamma cmsEstimateGamma = cmsEstimateGamma
cmsGetToneCurveEstimatedTableEntries = cmsGetToneCurveEstimatedTableEntries
cmsGetToneCurveEstimatedTable = cmsGetToneCurveEstimatedTable
cmsEvalToneCurve16 = cmsEvalToneCurve16 cmsEvalToneCurve16 = cmsEvalToneCurve16
cmsEvalToneCurveFloat = cmsEvalToneCurveFloat cmsEvalToneCurveFloat = cmsEvalToneCurveFloat
cmsfilelength = cmsfilelength cmsfilelength = cmsfilelength
@ -110,12 +114,12 @@ cmsGetPostScriptCRD = cmsGetPostScriptCRD
cmsGetPostScriptCSA = cmsGetPostScriptCSA cmsGetPostScriptCSA = cmsGetPostScriptCSA
cmsGetProfileInfo = cmsGetProfileInfo cmsGetProfileInfo = cmsGetProfileInfo
cmsGetProfileInfoASCII = cmsGetProfileInfoASCII cmsGetProfileInfoASCII = cmsGetProfileInfoASCII
cmsGetProfileContextID = cmsGetProfileContextID cmsGetProfileContextID = cmsGetProfileContextID
cmsGetProfileVersion = cmsGetProfileVersion cmsGetProfileVersion = cmsGetProfileVersion
cmsGetSupportedIntents = cmsGetSupportedIntents cmsGetSupportedIntents = cmsGetSupportedIntents
cmsGetTagCount = cmsGetTagCount cmsGetTagCount = cmsGetTagCount
cmsGetTagSignature = cmsGetTagSignature cmsGetTagSignature = cmsGetTagSignature
cmsGetTransformContextID = cmsGetTransformContextID cmsGetTransformContextID = cmsGetTransformContextID
_cmsICCcolorSpace = _cmsICCcolorSpace _cmsICCcolorSpace = _cmsICCcolorSpace
_cmsIOPrintf = _cmsIOPrintf _cmsIOPrintf = _cmsIOPrintf
cmsIsCLUT = cmsIsCLUT cmsIsCLUT = cmsIsCLUT
@ -131,15 +135,18 @@ cmsIT8Alloc = cmsIT8Alloc
cmsIT8DefineDblFormat = cmsIT8DefineDblFormat cmsIT8DefineDblFormat = cmsIT8DefineDblFormat
cmsIT8EnumDataFormat = cmsIT8EnumDataFormat cmsIT8EnumDataFormat = cmsIT8EnumDataFormat
cmsIT8EnumProperties = cmsIT8EnumProperties cmsIT8EnumProperties = cmsIT8EnumProperties
cmsIT8EnumPropertyMulti = cmsIT8EnumPropertyMulti
cmsIT8Free = cmsIT8Free cmsIT8Free = cmsIT8Free
cmsIT8GetData = cmsIT8GetData cmsIT8GetData = cmsIT8GetData
cmsIT8GetDataDbl = cmsIT8GetDataDbl cmsIT8GetDataDbl = cmsIT8GetDataDbl
cmsIT8FindDataFormat = cmsIT8FindDataFormat cmsIT8FindDataFormat = cmsIT8FindDataFormat
cmsIT8GetDataRowCol = cmsIT8GetDataRowCol cmsIT8GetDataRowCol = cmsIT8GetDataRowCol
cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl
cmsIT8GetPatchName = cmsIT8GetPatchName cmsIT8GetPatchName = cmsIT8GetPatchName
cmsIT8GetPatchByName = cmsIT8GetPatchByName
cmsIT8GetProperty = cmsIT8GetProperty cmsIT8GetProperty = cmsIT8GetProperty
cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl
cmsIT8GetPropertyMulti = cmsIT8GetPropertyMulti
cmsIT8GetSheetType = cmsIT8GetSheetType cmsIT8GetSheetType = cmsIT8GetSheetType
cmsIT8LoadFromFile = cmsIT8LoadFromFile cmsIT8LoadFromFile = cmsIT8LoadFromFile
cmsIT8LoadFromMem = cmsIT8LoadFromMem cmsIT8LoadFromMem = cmsIT8LoadFromMem
@ -154,10 +161,12 @@ cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl
cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl
cmsIT8SetPropertyHex = cmsIT8SetPropertyHex cmsIT8SetPropertyHex = cmsIT8SetPropertyHex
cmsIT8SetPropertyStr = cmsIT8SetPropertyStr cmsIT8SetPropertyStr = cmsIT8SetPropertyStr
cmsIT8SetPropertyMulti = cmsIT8SetPropertyMulti
cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked
cmsIT8SetSheetType = cmsIT8SetSheetType cmsIT8SetSheetType = cmsIT8SetSheetType
cmsIT8SetTable = cmsIT8SetTable cmsIT8SetTable = cmsIT8SetTable
cmsIT8SetTableByLabel = cmsIT8SetTableByLabel cmsIT8SetTableByLabel = cmsIT8SetTableByLabel
cmsIT8SetIndexColumn = cmsIT8SetIndexColumn
cmsIT8TableCount = cmsIT8TableCount cmsIT8TableCount = cmsIT8TableCount
cmsJoinToneCurve = cmsJoinToneCurve cmsJoinToneCurve = cmsJoinToneCurve
cmsLab2LCh = cmsLab2LCh cmsLab2LCh = cmsLab2LCh
@ -167,6 +176,7 @@ cmsLabEncoded2FloatV2 = cmsLabEncoded2FloatV2
cmsLCh2Lab = cmsLCh2Lab cmsLCh2Lab = cmsLCh2Lab
_cmsLCMScolorSpace = _cmsLCMScolorSpace _cmsLCMScolorSpace = _cmsLCMScolorSpace
cmsLinkTag = cmsLinkTag cmsLinkTag = cmsLinkTag
cmsTagLinkedTo = cmsTagLinkedTo
cmsPipelineAlloc = cmsPipelineAlloc cmsPipelineAlloc = cmsPipelineAlloc
cmsPipelineCat = cmsPipelineCat cmsPipelineCat = cmsPipelineCat
cmsPipelineCheckAndRetreiveStages = cmsPipelineCheckAndRetreiveStages cmsPipelineCheckAndRetreiveStages = cmsPipelineCheckAndRetreiveStages
@ -291,10 +301,41 @@ _cmsWriteUInt32Number = _cmsWriteUInt32Number
_cmsWriteUInt64Number = _cmsWriteUInt64Number _cmsWriteUInt64Number = _cmsWriteUInt64Number
_cmsWriteUInt8Number = _cmsWriteUInt8Number _cmsWriteUInt8Number = _cmsWriteUInt8Number
_cmsWriteXYZNumber = _cmsWriteXYZNumber _cmsWriteXYZNumber = _cmsWriteXYZNumber
cmsxyY2XYZ = cmsxyY2XYZ cmsxyY2XYZ = cmsxyY2XYZ
cmsXYZ2Lab = cmsXYZ2Lab cmsXYZ2Lab = cmsXYZ2Lab
cmsXYZ2xyY = cmsXYZ2xyY cmsXYZ2xyY = cmsXYZ2xyY
cmsXYZEncoded2Float = cmsXYZEncoded2Float cmsXYZEncoded2Float = cmsXYZEncoded2Float
cmsSliceSpace16 = cmsSliceSpace16 cmsSliceSpace16 = cmsSliceSpace16
cmsSliceSpaceFloat = cmsSliceSpaceFloat cmsSliceSpaceFloat = cmsSliceSpaceFloat
cmsChangeBuffersFormat = cmsChangeBuffersFormat 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 // 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 // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"), // a copy of this software and associated documentation files (the "Software"),
@ -46,15 +46,18 @@
# define M_LOG10E 0.434294481903251827651 # define M_LOG10E 0.434294481903251827651
#endif #endif
// BorlandC 5.5 & Visual Studio 2003 are broken on that // BorlandC 5.5, VC2003 are broken on that
#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1310)) #if defined(__BORLANDC__) || (_MSC_VER < 1400) // 1400 == VC++ 8.0
#define sinf(x) (float)sin((float)x) #define sinf(x) (float)sin((float)x)
#define sqrtf(x) (float)sqrt((float)x) #define sqrtf(x) (float)sqrt((float)x)
#endif #endif
// Alignment of ICC file format uses 4 bytes (cmsUInt32Number) // Alignment of ICC file format uses 4 bytes (cmsUInt32Number)
#define _cmsSIZEOFLONGMINUS1 (sizeof(cmsUInt32Number)-1) #define _cmsALIGNLONG(x) (((x)+(sizeof(cmsUInt32Number)-1)) & ~(sizeof(cmsUInt32Number)-1))
#define _cmsALIGNLONG(x) (((x)+_cmsSIZEOFLONGMINUS1) & ~(_cmsSIZEOFLONGMINUS1))
// Alignment to memory pointer
#define _cmsALIGNMEM(x) (((x)+(sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
// Maximum encodeable values in floating point // Maximum encodeable values in floating point
#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0) #define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0)
@ -64,7 +67,7 @@
#define MAX_ENCODEABLE_ab4 (127.0) #define MAX_ENCODEABLE_ab4 (127.0)
// Maximum of channels for internal pipeline evaluation // Maximum of channels for internal pipeline evaluation
#define MAX_STAGE_CHANNELS 128 #define MAX_STAGE_CHANNELS 128
// Unused parameter warning supression // Unused parameter warning supression
#define cmsUNUSED_PARAMETER(x) ((void)x) #define cmsUNUSED_PARAMETER(x) ((void)x)
@ -87,36 +90,6 @@
# endif # endif
#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 // A fast way to convert from/to 16 <-> 8 bits
#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) #define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb))
@ -191,42 +164,202 @@ cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d)
return _cmsQuickFloorWord(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. // 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 // Memory management
cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Interpolation // Interpolation
cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Parametric curves // Parametric curves
cmsBool _cmsRegisterParametricCurvesPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Formatters management // Formatters management
cmsBool _cmsRegisterFormattersPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Tag type management // Tag type management
cmsBool _cmsRegisterTagTypePlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Tag management // Tag management
cmsBool _cmsRegisterTagPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterTagPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Intent management // Intent management
cmsBool _cmsRegisterRenderingIntentPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Multi Process elements // Multi Process elements
cmsBool _cmsRegisterMultiProcessElementPlugin(cmsPluginBase* Plugin); cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
// Optimization // 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 { typedef struct _cmsSubAllocator_chunk_st {
cmsUInt8Number* Block; cmsUInt8Number* Block;
@ -249,9 +382,264 @@ typedef struct {
_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial); _cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial);
void _cmsSubAllocDestroy(_cmsSubAllocator* s); void _cmsSubAllocDestroy(_cmsSubAllocator* s);
void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size); 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 // MLU internal representation
typedef struct { typedef struct {
@ -259,7 +647,7 @@ typedef struct {
cmsUInt16Number Country; cmsUInt16Number Country;
cmsUInt32Number StrW; // Offset to current unicode string cmsUInt32Number StrW; // Offset to current unicode string
cmsUInt32Number Len; // Lenght in bytes cmsUInt32Number Len; // Length in bytes
} _cmsMLUentry; } _cmsMLUentry;
@ -326,9 +714,11 @@ typedef struct _cms_iccprofile_struct {
cmsColorSpaceSignature ColorSpace; cmsColorSpaceSignature ColorSpace;
cmsColorSpaceSignature PCS; cmsColorSpaceSignature PCS;
cmsUInt32Number RenderingIntent; cmsUInt32Number RenderingIntent;
cmsUInt32Number flags; cmsUInt32Number flags;
cmsUInt32Number manufacturer, model; cmsUInt32Number manufacturer, model;
cmsUInt64Number attributes; cmsUInt64Number attributes;
cmsUInt32Number creator;
cmsProfileID ProfileID; cmsProfileID ProfileID;
@ -341,10 +731,14 @@ typedef struct _cms_iccprofile_struct {
cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked
void * TagPtrs[MAX_TABLE_TAG]; void * TagPtrs[MAX_TABLE_TAG];
cmsTagTypeHandler* TagTypeHandlers[MAX_TABLE_TAG]; // Same structure may be serialized on different types 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 // Special
cmsBool IsWrite; cmsBool IsWrite;
// Keep a mutex for cmsReadTag -- Note that this only works if the user includes a mutex plugin
void * UsrMutex;
} _cmsICCPROFILE; } _cmsICCPROFILE;
// IO helpers for profiles // IO helpers for profiles
@ -353,9 +747,9 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSp
int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks); int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks);
// Tag types // Tag types
cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsTagTypeSignature sig); cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig);
cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig); cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig);
cmsTagDescriptor* _cmsGetTagDescriptor(cmsTagSignature sig); cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig);
// Error logging --------------------------------------------------------------------------------------------------------- // 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* _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); cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags);
void _cmsFreeInterpParams(cmsInterpParams* p); void _cmsFreeInterpParams(cmsInterpParams* p);
cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p); cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p);
// Curves ---------------------------------------------------------------------------------------------------------------- // Curves ----------------------------------------------------------------------------------------------------------------
@ -414,37 +808,6 @@ struct _cmsStage_struct {
struct _cmsStage_struct* Next; 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) // Special Stages (cannot be saved)
cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID); cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID);
@ -453,9 +816,13 @@ cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID); cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID); cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID);
cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID); cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID);
cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList); cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS);
cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels); cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels);
cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan); 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 // For curve set only
cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe); cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe);
@ -476,12 +843,12 @@ struct _cmsPipeline_struct {
_cmsOPTeval16Fn Eval16Fn; _cmsOPTeval16Fn Eval16Fn;
_cmsPipelineEvalFloatFn EvalFloatFn; _cmsPipelineEvalFloatFn EvalFloatFn;
_cmsOPTfreeDataFn FreeDataFn; _cmsFreeUserDataFn FreeDataFn;
_cmsOPTdupDataFn DupDataFn; _cmsDupUserDataFn DupDataFn;
cmsContext ContextID; // Environment 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 ------------------------------------------------------------------------------------------- // LUT reading & creation -------------------------------------------------------------------------------------------
@ -524,7 +891,8 @@ cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
cmsUInt16Number **Black, cmsUInt16Number **Black,
cmsUInt32Number *nOutputs); cmsUInt32Number *nOutputs);
cmsBool _cmsOptimizePipeline(cmsPipeline** Lut, cmsBool _cmsOptimizePipeline(cmsContext ContextID,
cmsPipeline** Lut,
int Intent, int Intent,
cmsUInt32Number* InputFormat, cmsUInt32Number* InputFormat,
cmsUInt32Number* OutputFormat, cmsUInt32Number* OutputFormat,
@ -549,26 +917,33 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID,
cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type); cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type);
cmsBool _cmsFormatterIs8bit(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, cmsFormatterDirection Dir,
cmsUInt32Number dwFlags); cmsUInt32Number dwFlags);
#ifndef CMS_NO_HALF_SUPPORT
// Half float
cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h);
cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt);
#endif
// Transform logic ------------------------------------------------------------------------------------------------------ // Transform logic ------------------------------------------------------------------------------------------------------
struct _cmstransform_struct; struct _cmstransform_struct;
// Full xform
typedef void (* _cmsTransformFn)(struct _cmstransform_struct *Transform,
const void* InputBuffer,
void* OutputBuffer, cmsUInt32Number Size);
typedef struct { typedef struct {
cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference // 1-pixel cache (16 bits only)
cmsUInt32Number StrideIn, StrideOut; // Planar support cmsUInt16Number CacheIn[cmsMAXCHANNELS];
cmsUInt16Number CacheOut[cmsMAXCHANNELS];
} _cmsCACHE;
} cmsFormatterInfo;
// Transformation // Transformation
typedef struct _cmstransform_struct { typedef struct _cmstransform_struct {
@ -585,17 +960,13 @@ typedef struct _cmstransform_struct {
cmsFormatterFloat FromInputFloat; cmsFormatterFloat FromInputFloat;
cmsFormatterFloat ToOutputFloat; cmsFormatterFloat ToOutputFloat;
// 1-pixel cache (16 bits only) // 1-pixel cache seed for zero as input (16 bits, read only)
cmsUInt16Number CacheIn[cmsMAXCHANNELS]; _cmsCACHE Cache;
cmsUInt16Number CacheOut[cmsMAXCHANNELS];
// Semaphor for cache // A Pipeline holding the full (optimized) transform
LCMS_RWLOCK_T rwlock;
// A MPE LUT holding the full (optimized) transform
cmsPipeline* Lut; 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; cmsPipeline* GamutCheck;
// Colorant tables // Colorant tables
@ -606,6 +977,10 @@ typedef struct _cmstransform_struct {
cmsColorSpaceSignature EntryColorSpace; cmsColorSpaceSignature EntryColorSpace;
cmsColorSpaceSignature ExitColorSpace; cmsColorSpaceSignature ExitColorSpace;
// White points (informative only)
cmsCIEXYZ EntryWhitePoint;
cmsCIEXYZ ExitWhitePoint;
// Profiles used to create the transform // Profiles used to create the transform
cmsSEQ* Sequence; cmsSEQ* Sequence;
@ -618,6 +993,10 @@ typedef struct _cmstransform_struct {
// An id that uniquely identifies the running context. May be null. // An id that uniquely identifies the running context. May be null.
cmsContext ContextID; cmsContext ContextID;
// A user-defined pointer that can be used to store data for transform plug-ins
void* UserData;
_cmsFreeUserDataFn FreeUserData;
} _cmsTRANSFORM; } _cmsTRANSFORM;
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------