Profiler API examples.
#include <assert.h>
#include <lcms2.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#ifndef OY_UNUSED
#ifdef __GNUC__
#define OY_UNUSED __attribute__ ((unused))
#elif defined(_MSC_VER)
#define OY_UNUSED __declspec(unused)
#else
#define OY_UNUSED __attribute__ ((unused))
#endif
#endif
#if LCMS_VERSION < 2050
#define cmsSigProfileDescriptionMLTag 0x6473636d
#endif
#define lcm2Free_m(v) if(v) { free(v); v = NULL; }
extern lcm2Message_f lcm2msg_p;
static const int max_channels = 16;
typedef struct {
cmsHTRANSFORM in2MySpace;
cmsHTRANSFORM mySpace2Out;
void * sampler_variables;
int channelsIn;
int channelsProcess;
int channelsOut;
} lcm2Cargo_s;
int lcm2samplerDouble ( double in[],
double out[],
void * Cargo )
{
int i;
lcm2Cargo_s * d = (lcm2Cargo_s*) Cargo;
if(d->in2MySpace)
cmsDoTransform( d->in2MySpace, in, in, 1 );
d->sampler(in,out,d->sampler_variables);
if(d->mySpace2Out)
cmsDoTransform( d->mySpace2Out, out, out, 1 );
for(i = 0; i < d->channelsOut; ++i)
{
if(out[i] > 1.0)
out[i] = 1.0;
if(out[i] < 0.0)
out[i] = 0.0;
}
return TRUE;
}
int lcm2sampler16 (const cmsUInt16Number In[],
cmsUInt16Number Out[],
void * Cargo)
{
int i, v, result = TRUE;
double in[max_channels], out[max_channels],
scaler = 65536.0;
lcm2Cargo_s * d = (lcm2Cargo_s*) Cargo;
for(i = 0; i < d->channelsIn; ++i)
in[i] = In[i] / scaler;
result = lcm2samplerDouble( in, out, Cargo );
for(i = 0; i < d->channelsOut; ++i)
{
v = out[i] * scaler;
if(v > 65535)
Out[i] = 65535;
else
Out[i] = v;
}
return result;
}
int lcm2samplerFloat ( const cmsFloat32Number In[],
cmsFloat32Number Out[],
void * Cargo )
{
int i, result = TRUE;
double in[max_channels], out[max_channels];
lcm2Cargo_s * d = (lcm2Cargo_s*) Cargo;
for(i = 0; i < d->channelsIn; ++i)
in[i] = In[i];
result = lcm2samplerDouble( in, out, Cargo );
for(i = 0; i < d->channelsOut; ++i)
Out[i] = out[i];
return result;
}
const char * my_space_profile_path )
{
cmsHPROFILE h_my_space = 0;
if(my_space_profile_path == NULL) my_space_profile_path = "";
if(my_space_profile && my_space_profile[0])
{
char * full_name = (char*) malloc(strlen(my_space_profile_path) + strlen(my_space_profile) + 1);
if(!full_name) return NULL;
sprintf( full_name, "%s%s", my_space_profile_path, my_space_profile );
if(strcmp(my_space_profile,"*lab") == 0)
h_my_space = cmsCreateLab4Profile(cmsD50_xyY());
else
if(strcmp(my_space_profile,"*xyz") == 0)
h_my_space = cmsCreateXYZProfile( );
else
if(strcmp(my_space_profile,"*srgb") == 0)
h_my_space = cmsCreate_sRGBProfile( );
else
if(strcmp(my_space_profile,"*srgblinear") == 0)
0.30, 0.60,
0.15, 0.06,
0.3127,0.329 );
else
if(strcmp(my_space_profile,"*rec601.625.linear") == 0)
0.29, 0.60,
0.15, 0.06,
0.3127,0.329 );
else
if(strcmp(my_space_profile,"*rec601.525.linear") == 0)
0.31, 0.595,
0.155, 0.07,
0.3127,0.329 );
if(!h_my_space)
h_my_space = cmsOpenProfileFromFile( full_name, "rb" );
if(!h_my_space) { lcm2msg_p( 300, NULL, "no profile from %s", full_name); }
lcm2Free_m(full_name);
}
return h_my_space;
}
const char * my_space_profile_name,
const char * my_space_profile_version,
const char * vendor_four_bytes )
{
int i;
i = 0;
char * fn = (char*) malloc(strlen(my_space_profile_name) +
(my_space_profile_version ? strlen(my_space_profile_version):0) +
(vendor_four_bytes ? strlen(vendor_four_bytes):0) + 8);
if(!fn) return fn;
sprintf( fn, "%s%s%s%s%s%s", my_space_profile_name,
my_space_profile_version ? " " : "", my_space_profile_version?my_space_profile_version:"",
vendor_four_bytes ? " " : "", vendor_four_bytes?vendor_four_bytes:"",
strstr(my_space_profile_name, ".icc") ? "" : ".icc" );
while(fn[i]) { if(fn[i] == ' ') fn[i] = '_'; ++i; }
cmsSaveProfileToFile( my_space_profile, fn );
return fn;
}
size_t * size,
void * (*allocateFunc)(size_t size) )
{
int error = !profile;
void * data = 0;
cmsUInt32Number size_ = 0;
if(!error)
{
*size = 0;
if(!cmsSaveProfileToMem( profile, NULL, &size_ ))
lcm2msg_p( 300, NULL, "cmsSaveProfileToMem failed" );
if(size_)
{
if(allocateFunc)
data = allocateFunc( size_ );
else
data = malloc( size_ );
cmsSaveProfileToMem( profile, data, &size_ );
} else
lcm2msg_p( 300, NULL, "can not convert lcms2 profile to memory" );
*size = size_;
} else
lcm2msg_p( 301, NULL, "no profle" );
return data;
}
static double CIE_C_scaler = M_SQRT2;
double o[],
void * none OY_UNUSED )
{
double a = (i[1] - 0.5) * CIE_C_scaler,
b = (i[2] - 0.5) * CIE_C_scaler;
o[0] = i[0];
o[1] = hypot(a,b);
o[2] = atan2(b,a)/M_PI/2.0 + 0.5;
}
double o[],
void * none OY_UNUSED )
{
o[0] = i[0];
o[1] = i[1] * cos(M_PI*2.0*i[2]) / CIE_C_scaler + 0.5;
o[2] = i[1] * sin(M_PI*2.0*i[2]) / CIE_C_scaler + 0.5;
}
typedef enum {
ITU_R_BT_601,
ITU_R_BT_601_JPEG,
ITU_REC_709,
ITU_R_BT_2020
} ITU_Std_e;
const char * ITU_Std_dscr [] = { "ITU-R BT.601", "ITU-R BT.601 / JPEG", "ITU REC-709", "ITU-R BT.2020", NULL };
static void selectKbKr( ITU_Std_e ITU_Std, double * Kb, double * Kr )
{
switch(ITU_Std)
{
case ITU_R_BT_601:
case ITU_R_BT_601_JPEG:
*Kb = 0.114;
*Kr = 0.299;
break;
case ITU_REC_709:
*Kb = 0.0722;
*Kr = 0.2126;
break;
case ITU_R_BT_2020:
*Kb = 0.0593;
*Kr = 0.2627;
break;
}
}
void selectBlackScale( ITU_Std_e ITU_Std, double * black, double * scale )
{
switch(ITU_Std)
{
case ITU_R_BT_601_JPEG:
*black = 0;
*scale = 255;
break;
case ITU_R_BT_601:
case ITU_REC_709:
case ITU_R_BT_2020:
*black = 16;
*scale = 219;
break;
}
}
void linear2ycbcr( double *L_ )
{
double L = *L_;
double alpha = 1.09929682680944,
beta = 0.018053968510807;
if(L < beta)
L *= 4.5;
else
L = pow(L,0.45) - (alpha - 1);
*L_ = L;
}
void ycbcr2linear( double *V_ )
{
double L = *V_;
double alpha = 1.09929682680944,
beta = 0.081243;
if(L < beta)
L /= 4.5;
else
L = pow( (L + (alpha-1)) / alpha, 1.0/0.45 );
*V_ = L;
}
static void rgb2ycbcr( double R, double G, double B,
double *Y_, double *Pb_, double *Pr_,
double Kb, double Kr )
{
double Y,Pb,Pr;
Y = Kr * R + (1.0-Kr-Kb) * G + Kb * B;
Pb = 1.0/2.0 * (B-Y)/(1.0-Kb);
Pr = 1.0/2.0 * (R-Y)/(1.0-Kr);
*Y_ = Y; *Pb_ = Pb; *Pr_ = Pr;
}
static void ycbcr2rgb( double Y, double Pb, double Pr,
double *R_, double *G_, double *B_,
double Kb, double Kr )
{
double R,G,B;
B = 2*Pb*(1-Kb) + Y;
R = 2*Pr*(1-Kr) + Y;
G = (Y - Kr*R - Kb*B)/(1.0-Kb-Kr);
*R_ = R; *G_ = G; *B_ = B;
}
static void scaleRGB( ITU_Std_e ITU_Std, double scale, double * R, double * G, double * B )
{
switch(ITU_Std)
{
case ITU_R_BT_601:
case ITU_REC_709:
case ITU_R_BT_2020:
case ITU_R_BT_601_JPEG:
*R *= scale;
*G *= scale;
*B *= scale;
break;
}
}
static void scaleLinearToYCbCr( ITU_Std_e ITU_Std, double max, double * Y, double * Cb, double * Cr )
{
max /= 255.0;
switch(ITU_Std)
{
case ITU_R_BT_601:
case ITU_REC_709:
case ITU_R_BT_2020:
*Y *= (235.*max-16.*max);
*Y += 16.*max;
*Cb *= (240.*max-16.*max);
*Cb += 128.*max;
*Cr *= (240.*max-16.*max);
*Cr += 128.*max;
break;
case ITU_R_BT_601_JPEG:
*Y *= 255.*max;
*Cb *= 255.*max;
*Cb += 128.*max;
*Cr *= 255.*max;
*Cr += 128.*max;
break;
}
}
static void scaleYCbCrToLinear( ITU_Std_e ITU_Std, double max, double * Y, double * Cb, double * Cr )
{
max /= 255.0;
switch(ITU_Std)
{
case ITU_R_BT_601:
case ITU_REC_709:
case ITU_R_BT_2020:
*Y -= 16.*max;
*Y /= (235.*max-16.*max);
*Cb -= 128.*max;
*Cb /= (240.*max-16.*max);
*Cr -= 128.*max;
*Cr /= (240.*max-16.*max);
break;
case ITU_R_BT_601_JPEG:
*Y /= 255.*max;
*Cb -= 128.*max;
*Cb /= 255.*max;
*Cr -= 128.*max;
*Cr /= 255.*max;
break;
}
}
const double i[],
double o[],
void * none OY_UNUSED )
{
ITU_Std_e
std = ITU_R_BT_601_JPEG;
double Kr,Kb,
Y = i[0], Pb = i[1], Pr = i[2],
R = i[0], G = i[1], B = i[2];
selectKbKr( std, &Kb, &Kr );
scaleRGB( std, 1.0, &R, &G, &B );
rgb2ycbcr( R, G, B, &Y, &Pb, &Pr, Kb,Kr );
scaleLinearToYCbCr( std, 1.0, &Y, &Pb, &Pr );
o[0] = Y; o[1] = Pb; o[2] = Pr;
}
double o[],
void * none OY_UNUSED )
{
ITU_Std_e
std = ITU_R_BT_601_JPEG;
double Kr,Kb,
Y = i[0], Pb = i[1], Pr = i[2],
R,G,B;
selectKbKr( std, &Kb, &Kr );
scaleYCbCrToLinear( std, 1.0, &Y, &Pb, &Pr );
ycbcr2rgb( Y, Pb, Pr, &R, &G, &B, Kb,Kr );
scaleRGB( std, 1.0, &R, &G, &B );
o[0] = R; o[1] = G; o[2] = B;
}
double o[],
void * none OY_UNUSED )
{
o[0] = i[0]*1.0;
o[1] = 0.5;
o[2] = 0.5;
}
double o[],
void * none OY_UNUSED )
{
if(i[0] < 0.5)
o[0] = 0.0;
else
o[0] = 1.0;
o[1] = 0.5;
o[2] = 0.5;
}
double o[],
void * none )
{
double in[3],out[3];
out[0] = in[0];
out[1] = 0.04+0.04*in[0];
out[2] = 0.18;
}
double o[],
void * none OY_UNUSED )
{
o[0] = i[0];
o[1] = i[1] + 0.012+0.012*i[0];
o[2] = i[2] + 0.025+0.025*i[0];
}
double o[],
void * data )
{
double * icc_ab = (double*) data;
o[0] = i[0];
o[1] = i[1] + icc_ab[0] * i[0];
o[2] = i[2] + icc_ab[1] * i[0];
}
double o[],
void * data )
{
cmsCIELab Lab1, Lab2;
double d;
cmsFloat32Number i_[3], o_[3];
void ** ptr = (void**)data;
i_[0] = Lab1.L = i[0] * 100.0;
i_[1] = Lab1.a = i[1] * 257.0 - 128.0;
i_[2] = Lab1.b = i[2] * 257.0 - 128.0;
cmsDoTransform( ptr[0], i_, o_, 1 );
Lab2.L = o_[0]; Lab2.a = o_[1]; Lab2.b = o_[2];
d = cmsDeltaE( &Lab1, &Lab2 );
if((fabs(d) > 10) && ptr[1] != NULL)
{
Lab2.L = 50.0;
Lab2.a = Lab2.b = 0.0;
}
o[0] = Lab2.L/100.0;
o[1] = (Lab2.a + 128.0) / 257.0;
o[2] = (Lab2.b + 128.0) / 257.0;
}
cmsHPROFILE profile,
void * samplerArg,
const char * in_space_profile,
const char * my_space_profile,
const char * out_space_profile,
int grid_size,
cmsTagSignature tag_sig
)
{
cmsToneCurve * t[max_channels];
int i;
int error = 0;
if(!profile) return 1;
t[0] = cmsBuildGamma(0, 1.0);
if(!t[0]) return 1;
for(i = 1; i < max_channels; ++i) t[i] = t[0];
profile,
samplerMySpace,
samplerArg,
t, t,
in_space_profile,
my_space_profile,
out_space_profile,
grid_size, tag_sig
);
cmsFreeToneCurve( t[0] );
return error;
}
cmsHPROFILE profile,
void * samplerArg,
cmsToneCurve * in_curves[],
cmsToneCurve * out_curves[],
const char * in_space_profile,
const char * my_space_profile,
const char * out_space_profile,
int grid_size,
cmsTagSignature tag_sig
)
{
cmsHPROFILE h_in_space = 0,
h_my_space = 0,
h_out_space = 0;
cmsHTRANSFORM tr_In2MySpace = 0, tr_MySpace2Out = 0;
cmsStage * gmt_lut = 0, * gmt_lut16 = 0;
cmsPipeline * gmt_pl = cmsPipelineAlloc( 0,3,3 ),
* gmt_pl16 = cmsPipelineAlloc( 0,3,3 );
lcm2Cargo_s cargo;
int i;
int error = 0;
int in_layout, my_layout, out_layout;
in_layout = my_layout = out_layout = (FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(0));
if(!profile) return 1;
if(h_in_space && h_my_space && strcmp(in_space_profile,my_space_profile) != 0)
{
tr_In2MySpace = cmsCreateTransformTHR ( 0, h_in_space, in_layout,
h_my_space, my_layout,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOOPTIMIZE);
if(!tr_In2MySpace) { lcm2msg_p( 300, NULL, "no transform"); error = 1; goto lcm2CreateProfileLutByFuncAndCurvesClean; }
}
if(h_my_space && h_out_space && strcmp(my_space_profile,out_space_profile) != 0)
{
tr_MySpace2Out = cmsCreateTransformTHR( 0, h_my_space, my_layout,
h_out_space, out_layout,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOOPTIMIZE);
if(!tr_MySpace2Out) { lcm2msg_p( 300, NULL, "no transform"); error = 1; goto lcm2CreateProfileLutByFuncAndCurvesClean; }
}
memset(&cargo, 0, sizeof(lcm2Cargo_s));
cargo.in2MySpace = tr_In2MySpace;
cargo.mySpace2Out = tr_MySpace2Out;
cargo.sampler = samplerMySpace;
cargo.sampler_variables = samplerArg,
cargo.channelsIn = h_in_space ? cmsChannelsOf( cmsGetColorSpace( h_in_space ) ) : 3;
cargo.channelsProcess = h_my_space ? cmsChannelsOf( cmsGetColorSpace( h_my_space ) ) : 3;
cargo.channelsOut = h_out_space ? cmsChannelsOf( cmsGetColorSpace( h_out_space ) ) : 3;
#pragma omp parallel for
for(i = 0; i < 2; ++i)
{
if(i)
{
gmt_lut16 = cmsStageAllocCLut16bit( 0, grid_size, 3,3,0 );
cmsStageSampleCLut16bit( gmt_lut16, lcm2sampler16, &cargo, 0 );
} else
{
gmt_lut = cmsStageAllocCLutFloat( 0, grid_size, 3,3,0 );
cmsStageSampleCLutFloat( gmt_lut, lcm2samplerFloat, &cargo, 0 );
}
}
cmsPipelineInsertStage( gmt_pl16, cmsAT_BEGIN,
cmsStageAllocToneCurves( 0, cargo.channelsIn, in_curves ) );
cmsPipelineInsertStage( gmt_pl16, cmsAT_END, gmt_lut16 );
cmsPipelineInsertStage( gmt_pl16, cmsAT_END,
cmsStageAllocToneCurves( 0, cargo.channelsOut, out_curves ) );
cmsWriteTag( profile, (tag_sig!=0)?tag_sig:cmsSigAToB0Tag, gmt_pl16 );
cmsPipelineInsertStage( gmt_pl, cmsAT_BEGIN,
cmsStageAllocToneCurves( 0, cargo.channelsIn, in_curves ) );
cmsPipelineInsertStage( gmt_pl, cmsAT_END, gmt_lut );
cmsPipelineInsertStage( gmt_pl, cmsAT_END,
cmsStageAllocToneCurves( 0, cargo.channelsOut, out_curves ) );
lcm2CreateProfileLutByFuncAndCurvesClean:
if(h_in_space) {cmsCloseProfile( h_in_space );} h_in_space = 0;
if(h_my_space) {cmsCloseProfile( h_my_space );} h_my_space = 0;
if(h_out_space) {cmsCloseProfile( h_out_space );} h_out_space = 0;
if(tr_In2MySpace) {cmsDeleteTransform( tr_In2MySpace );} tr_In2MySpace = 0;
if(tr_MySpace2Out) {cmsDeleteTransform( tr_MySpace2Out );} tr_MySpace2Out = 0;
if(gmt_pl16) cmsPipelineFree( gmt_pl16 );
if(gmt_pl) cmsPipelineFree( gmt_pl );
return error;
}
void * samplerArg,
const char * my_space_profile,
int grid_size,
double icc_profile_version,
const char * my_abstract_description,
const char ** my_abstract_descriptions,
const char * my_abstract_file_name,
const char * provider,
const char * vendor,
const char * my_license,
const char * device_model,
const char * device_manufacturer,
const char ** my_meta_data,
cmsHPROFILE * h_profile
)
{
cmsHPROFILE profile = 0;
int error = 0;
"*lab",
"*lab",
icc_profile_version,
my_abstract_description,
provider, vendor, my_license,
device_model, device_manufacturer, NULL);
if(!profile) goto lcm2CreateAbstractProfileClean;
if(my_meta_data)
"*lab", my_space_profile, "*lab",
grid_size, cmsSigAToB0Tag );
if(error) goto lcm2CreateAbstractProfileClean;
cmsSigProfileDescriptionMLTag
);
if(my_abstract_file_name)
{
lcm2msg_p( 302, NULL, "wrote to: %s", fn?fn:"----");
lcm2Free_m(fn);
}
if(h_profile)
*h_profile = profile;
else
cmsCloseProfile( profile );
lcm2CreateAbstractProfileClean:
return error;
}
float kelvin,
cmsHPROFILE source_white_profile,
int grid_size,
double icc_profile_version,
char ** my_abstract_file_name,
cmsHPROFILE * h_profile
)
{
cmsHPROFILE profile = NULL;
cmsToneCurve * i_curve[3] = {NULL,NULL,NULL}, * o_curve[3] = {NULL,NULL,NULL};
double curve_params[4] = {1,1,0,0}, curve_params_low[4] = {1,0.95,0,0};
int i;
cmsCIEXYZ * source_white = NULL;
const char * kelvin_meta[] = {
"EFFECT_class", "reddish,white_point,atom",
"COLORIMETRY_white_point", "yes,reddish,kelvin",
"CMF_binary", "create-abstract",
"CMF_version", "0.9.7",
"CMF_product", "Oyranos",
0,0
};
char * kelvin_name = malloc(1024);
int error = !kelvin_name;
double icc_ab[2];
char * desc = NULL;
if(error) return 1;
if(source_white_profile)
{
if(cmsIsTag(source_white_profile, cmsSigProfileDescriptionTag))
{
cmsUInt32Number n = cmsGetProfileInfoASCII(source_white_profile, cmsInfoDescription, cmsNoLanguage, cmsNoCountry, NULL, 0);
if(n)
{
desc = calloc( n+1, sizeof(char) );
if(!desc) goto lcm2CreateAbstractTemperatureProfileClean;
cmsUInt32Number nr = cmsGetProfileInfoASCII(source_white_profile, cmsInfoDescription, cmsNoLanguage, cmsNoCountry, desc, n);
if(n != nr)
lcm2msg_p( 301, NULL, "found propblem reading desc tag: %d %d", n,nr);
}
}
source_white = cmsReadTag( source_white_profile, cmsSigMediaWhitePointTag );
}
i_curve[0] = o_curve[0] = cmsBuildGamma(0, 1.0);
if(!i_curve[0]) error = 1;
for(i = 1; i < 3; ++i) { i_curve[i] = i_curve[0]; }
if(!error)
{
cmsCIExyY xyWhitePoint;
cmsFloat64Number TempK = kelvin;
cmsWhitePointFromTemp( &xyWhitePoint, TempK );
cmsCIEXYZ WhitePoint;
const cmsCIEXYZ * reference_white = cmsD50_XYZ();
float max_brightness;
cmsxyY2XYZ( &WhitePoint, &xyWhitePoint );
cmsCIELab LabWhitePoint;
cmsCIELab SrcLabWhitePoint;
if(source_white)
reference_white = source_white;
cmsXYZ2Lab( reference_white, &LabWhitePoint, &WhitePoint );
icc_ab[0] = LabWhitePoint.a/128.0;
icc_ab[1] = LabWhitePoint.b/128.0;
#ifndef OY_HYP
#define OY_SQRT(a,b) ((a)*(a) + (b)*(b))
#define OY_HYP(a,b) pow(OY_SQRT(a,b),1.0/2.0)
#endif
max_brightness = 1.0 - OY_HYP(icc_ab[0],icc_ab[1]);
cmsXYZ2Lab( cmsD50_XYZ(), &SrcLabWhitePoint, reference_white );
cmsXYZ2Lab( cmsD50_XYZ(), &LabWhitePoint, &WhitePoint );
lcm2msg_p( 302, NULL, "SrcW: %g %g %g LabW: %g %g %g diff: %g %g max brightness: %g",
SrcLabWhitePoint.L, SrcLabWhitePoint.a, SrcLabWhitePoint.b,
LabWhitePoint.L, LabWhitePoint.a, LabWhitePoint.b,
icc_ab[0], icc_ab[1], max_brightness );
curve_params_low[1] = max_brightness;
o_curve[0] = cmsBuildParametricToneCurve(0, 6, curve_params_low);
o_curve[1] = o_curve[2] = cmsBuildParametricToneCurve(0, 6, curve_params);
if(!o_curve[0] || !o_curve[1]) error = 1;
}
if(error) goto lcm2CreateAbstractTemperatureProfileClean;
if(icc_ab[1] > 0)
{
sprintf( kelvin_name, "Reddish %d K (www.oyranos.org)", (int)kelvin );
} else if(icc_ab[1] == 0) {
sprintf( kelvin_name, "%d K (www.oyranos.org)", (int)kelvin );
kelvin_meta[1] = "neutral,white_point,atom";
kelvin_meta[3] = "yes,D50,kelvin";
} else {
sprintf( kelvin_name, "Bluish %d K (www.oyranos.org)", (int)kelvin );
kelvin_meta[1] = "bluish,white_point,atom";
kelvin_meta[3] = "yes,bluish,kelvin";
}
if(source_white_profile)
{
if(desc && strlen(desc) < 900)
sprintf( &kelvin_name[strlen(kelvin_name)], " - %s", desc);
if(icc_ab[1] > 0)
{
kelvin_meta[1] = "reddish,white_point,atom,device";
kelvin_meta[3] = "yes,reddish,kelvin";
} else if(icc_ab[1] == 0) {
kelvin_meta[1] = "neutral,white_point,atom,device";
kelvin_meta[3] = "yes,D50,kelvin";
} else {
kelvin_meta[1] = "bluish,white_point,atom,device";
kelvin_meta[3] = "yes,bluish,kelvin";
}
}
if(!error)
"*lab",
"*lab",
icc_profile_version,
kelvin_name,
"Oyranos project 2017",
"Kai-Uwe Behrmann",
"CIE*Lab",
"http://www.cie.co.at",
NULL);
if(!profile) error = 1;
if(!error)
o_curve, i_curve,
"*lab", "*lab", "*lab",
grid_size, cmsSigAToB0Tag );
if(!error)
lcm2AddMetaTexts ( profile,
"EFFECT_,COLORIMETRY_,CMF_", kelvin_meta, cmsSigMetaTag );
lcm2CreateAbstractTemperatureProfileClean:
if(i_curve[0]) cmsFreeToneCurve( i_curve[0] );
if(o_curve[0]) cmsFreeToneCurve( o_curve[0] );
if(o_curve[1]) cmsFreeToneCurve( o_curve[1] );
*my_abstract_file_name = kelvin_name;
if(h_profile)
*h_profile = profile;
else if(profile && *my_abstract_file_name)
{
lcm2msg_p( 302, NULL, "wrote to: %s", fn?fn:"----");
lcm2Free_m(fn);
cmsCloseProfile( profile );
}
return error;
}
double cie_a,
double cie_b,
int grid_size,
double icc_profile_version,
char ** my_abstract_file_name,
cmsHPROFILE * h_profile
)
{
cmsHPROFILE profile = NULL;
cmsToneCurve * i_curve[3] = {NULL,NULL,NULL}, * o_curve[3] = {NULL,NULL,NULL};
double curve_params[4] = {1,1,0,0}, curve_params_low[4] = {1,0.95,0,0};
int i;
const char * kelvin_meta[] = {
"EFFECT_class", "reddish,white_point,atom",
"COLORIMETRY_white_point", "yes,reddish,kelvin",
"CMF_binary", "create-abstract",
"CMF_version", "0.9.7",
"CMF_product", "Oyranos",
0,0
};
char * kelvin_name = malloc(1024);
int error = !kelvin_name;
double icc_ab[2] = {cie_a, cie_b};
if(error) return 1;
i_curve[0] = cmsBuildGamma(0, 1.0);
if(!i_curve[0]) error = 1;
for(i = 1; i < 3; ++i)
{ i_curve[i] = i_curve[0]; }
if(!error)
{
#ifndef OY_HYP
#define OY_SQRT(a,b) ((a)*(a) + (b)*(b))
#define OY_HYP(a,b) pow(OY_SQRT(a,b),1.0/2.0)
#endif
double max_brightness = 1.0 - OY_HYP(icc_ab[0],icc_ab[1]);
curve_params_low[1] = max_brightness;
o_curve[0] = cmsBuildParametricToneCurve(0, 6, curve_params_low);
o_curve[1] = o_curve[2] = cmsBuildParametricToneCurve(0, 6, curve_params);
if(!o_curve[0] || !o_curve[1]) error = 1;
}
if(error) goto lcm2CreateAbstractWhitePointProfileClean;
if(icc_ab[1] > 0)
{
sprintf( kelvin_name, "Reddish CIE*a %g CIE*b %g", cie_a, cie_b );
} else if(-0.001 < icc_ab[1] && icc_ab[0] < 0.001) {
sprintf( kelvin_name, "CIE*a %g CIE*b %g", cie_a, cie_b );
kelvin_meta[1] = "neutral,white_point,atom";
kelvin_meta[3] = "yes,D50,kelvin";
} else {
sprintf( kelvin_name, "Bluish CIE*a %g CIE*b %g", cie_a, cie_b );
kelvin_meta[1] = "bluish,white_point,atom";
kelvin_meta[3] = "yes,bluish,kelvin";
}
"*lab",
"*lab",
icc_profile_version,
kelvin_name,
"Oyranos project 2017",
"Kai-Uwe Behrmann",
"CIE*Lab",
"http://www.cie.co.at",
NULL);
if(!profile) goto lcm2CreateAbstractWhitePointProfileClean;
o_curve, i_curve,
"*lab", "*lab", "*lab",
grid_size, cmsSigAToB0Tag );
if(!error)
lcm2AddMetaTexts ( profile,
"EFFECT_,COLORIMETRY_,CMF_", kelvin_meta, cmsSigMetaTag );
lcm2CreateAbstractWhitePointProfileClean:
if(i_curve[0]) cmsFreeToneCurve( i_curve[0] );
if(o_curve[0]) cmsFreeToneCurve( o_curve[0] );
if(o_curve[1]) cmsFreeToneCurve( o_curve[1] );
*my_abstract_file_name = kelvin_name;
if(h_profile)
*h_profile = profile;
else if(profile && *my_abstract_file_name)
{
lcm2msg_p( 302, NULL, "wrote to: %s", fn?fn:"----");
lcm2Free_m(fn);
cmsCloseProfile( profile );
}
return error;
}
const char * in_space_profile,
const char * out_space_profile,
double icc_profile_version,
const char * my_abstract_description,
const char * provider,
const char * vendor,
const char * my_license,
const char * device_model,
const char * device_manufacturer,
cmsHPROFILE h_profile
)
{
cmsHPROFILE h_in_space = 0,
h_out_space = 0;
cmsColorSpaceSignature csp_in, csp_out;
cmsProfileClassSignature profile_class = cmsSigAbstractClass;
cmsMLU * mlu[4] = {0,0,0,0};
int i;
char * license = NULL;
if(!h_profile)
{ h_profile = cmsCreateProfilePlaceholder( 0 ); } if(!h_profile) goto lcm2CreateProfileFragmentClean;
csp_in = cmsGetColorSpace( h_in_space );
csp_out = cmsGetColorSpace( h_out_space );
cmsSetProfileVersion( h_profile, icc_profile_version );
#define CSP_IS_PCS(csp) (csp == cmsSigLabData || csp == cmsSigXYZData)
if( CSP_IS_PCS(csp_in) && CSP_IS_PCS(csp_out) )
profile_class = cmsSigAbstractClass;
else if( CSP_IS_PCS(csp_out) )
profile_class = cmsSigInputClass;
else if( CSP_IS_PCS(csp_in) )
profile_class = cmsSigOutputClass;
else
profile_class = cmsSigLinkClass;
cmsSetDeviceClass( h_profile, profile_class );
cmsSetColorSpace( h_profile, csp_in );
cmsSetPCS( h_profile, csp_out );
for(i = 0; i < 4; ++i)
mlu[i] = cmsMLUalloc(0,1);
if(!(mlu[0] && mlu[1] && mlu[2] && mlu[3]))
return h_profile;
cmsMLUsetASCII(mlu[0], "EN", "us", my_abstract_description);
cmsWriteTag( h_profile, cmsSigProfileDescriptionTag, mlu[0] );
if(device_model)
{
cmsMLUsetASCII(mlu[1], "EN", "us", device_model);
cmsWriteTag( h_profile, cmsSigDeviceModelDescTag, mlu[1]);
}
if(device_manufacturer)
{
cmsMLUsetASCII(mlu[2], "EN", "us", device_manufacturer);
cmsWriteTag( h_profile, cmsSigDeviceMfgDescTag, mlu[2]);
}
license = (char *) malloc( strlen(my_license) + strlen(provider) + strlen(vendor) + 1 );
if(!license) goto lcm2CreateProfileFragmentClean;;
sprintf( license, my_license, provider, vendor );
cmsMLUsetASCII(mlu[3], "EN", "us", license);
cmsWriteTag( h_profile, cmsSigCopyrightTag, mlu[3]);
cmsWriteTag( h_profile, cmsSigMediaWhitePointTag, cmsD50_XYZ() );
lcm2CreateProfileFragmentClean:
if(h_in_space) { cmsCloseProfile( h_in_space ); } h_in_space = 0;
if(h_out_space) { cmsCloseProfile( h_out_space ); } h_out_space = 0;
for(i = 0; i < 4; ++i)
cmsMLUfree( mlu[i] );
lcm2Free_m(license);
return h_profile;
}
int isBigEndian ()
{ union { unsigned short u16; unsigned char c; } test = { .u16 = 1 }; return !test.c; }
typedef uint32_t UTF32;
typedef uint16_t UTF16;
typedef uint8_t UTF8;
typedef unsigned char Boolean;
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
typedef enum {
conversionOK,
sourceExhausted,
targetExhausted,
sourceIllegal
} lcm2UtfConversionResult;
typedef enum {
strictConversion = 0,
lenientConversion
} lcm2UtfConversionFlags;
static const int halfShift = 10;
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#define false 0
#define true 1
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
static Boolean isLegalUTF8(const UTF8 *source, int length)
{
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; OY_FALLTHROUGH
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; OY_FALLTHROUGH
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source) {
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false; OY_FALLTHROUGH
} OY_FALLTHROUGH
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4) return false;
return true;
}
lcm2UtfConversionResult lcm2ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, lcm2UtfConversionFlags flags)
{
lcm2UtfConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6; OY_FALLTHROUGH
case 4: ch += *source++; ch <<= 6; OY_FALLTHROUGH
case 3: ch += *source++; ch <<= 6; OY_FALLTHROUGH
case 2: ch += *source++; ch <<= 6; OY_FALLTHROUGH
case 1: ch += *source++; ch <<= 6; OY_FALLTHROUGH
case 0: ch += *source++; OY_FALLTHROUGH
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1);
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) {
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1);
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch;
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1);
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1);
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
wchar_t * lcm2Utf8ToWchar ( const char * text )
{
wchar_t * wchar_out, * tmp_out;
char * in, * tmp_in;
size_t in_len = strlen(text),
out_len = in_len*sizeof(wchar_t)+sizeof(wchar_t);
lcm2UtfConversionResult error;
if(!in_len) return 0;
else ++in_len;
tmp_out = wchar_out = calloc( in_len+1, sizeof(wchar_t) );
in = tmp_in = strdup( text );
error = lcm2ConvertUTF8toUTF16( (const UTF8**)&in, (const UTF8*)in+in_len, (UTF16**)&tmp_out, (UTF16*)(tmp_out+out_len), lenientConversion );
if(error != conversionOK)
{
lcm2msg_p( 300, NULL, "error[%d] %lu %lu %s", error, in_len, out_len, text );
lcm2Free_m(wchar_out);
}
lcm2Free_m( tmp_in );
return wchar_out;
}
const char * texts[],
cmsTagSignature tag_sig )
{
int n = 0, i;
cmsMLU * mlu = NULL;
if(texts)
while( texts[n] ) ++n;
if(!n) return;
mlu = cmsMLUalloc( 0, n/3 + 1 );
if(!mlu) return;
for( i = 0; i < n; i += 3 )
{
char lang[4] = {0,0,0,0}, country[4] = {0,0,0,0};
const char * text = texts[i+2];
wchar_t * wchar_out;
wchar_out = lcm2Utf8ToWchar( text );
if(!wchar_out) continue;
lang[0] = texts[i+0][0]; lang[1] = texts[i+0][1];
country[0] = texts[i+1][0]; country[1] = texts[i+1][1];
cmsMLUsetWide( mlu, lang, country, wchar_out );
lcm2Free_m( wchar_out );
}
cmsWriteTag( profile, tag_sig, mlu );
cmsMLUfree( mlu );
}
const char * prefixes,
const char * key_value[],
cmsTagSignature tag_sig )
{
int n = 0, i;
cmsHANDLE dict = NULL;
cmsContext contextID = cmsCreateContext( NULL,NULL );
wchar_t * wchar_key = NULL, * wchar_val = NULL;
if(key_value)
while( key_value[n] ) ++n;
if(n)
dict = cmsDictAlloc( contextID );
else
lcm2msg_p( 300, NULL, "nothing to write %s", __func__ );
if(!dict)
return;
if(prefixes)
{
wchar_key = lcm2Utf8ToWchar( "prefix" );
wchar_val = lcm2Utf8ToWchar( prefixes );
}
if(wchar_key && wchar_val)
cmsDictAddEntry( dict, wchar_key, wchar_val, NULL,NULL );
lcm2Free_m( wchar_key );
lcm2Free_m( wchar_val );
for( i = 0; i < n; i += 2 )
{
const char * key = key_value[i+0],
* val = key_value[i+1];
wchar_key = lcm2Utf8ToWchar(key),
wchar_val = lcm2Utf8ToWchar(val);
if(!wchar_key || !wchar_val)
{
lcm2Free_m( wchar_key );
lcm2Free_m( wchar_val );
continue;
}
cmsDictAddEntry( dict, wchar_key, wchar_val, NULL,NULL );
lcm2Free_m( wchar_key );
lcm2Free_m( wchar_val );
}
cmsWriteTag( profile, tag_sig, dict );
cmsDictFree( dict );
}
float gamma,
float rx, float ry,
float gx, float gy,
float bx, float by,
float wx, float wy )
{
cmsCIExyYTRIPLE p;
cmsToneCurve * g[3] = {0,0,0};
cmsCIExyY wtpt_xyY;
cmsHPROFILE lp = 0;
p.Red.x = rx;
p.Red.y = ry;
p.Red.Y = 1.0;
p.Green.x = gx;
p.Green.y = gy;
p.Green.Y = 1.0;
p.Blue.x = bx;
p.Blue.y = by;
p.Blue.Y = 1.0;
wtpt_xyY.x = wx;
wtpt_xyY.y = wy;
wtpt_xyY.Y = 1.0;
g[0] = g[1] = g[2] = cmsBuildGamma(0, (double)gamma);
if(!g[0]) return NULL;
lp = cmsCreateRGBProfile( &wtpt_xyY, &p, g);
cmsFreeToneCurve( g[0] );
return lp;
}
const void * context_object OY_UNUSED,
const char * format,
... )
{
char * text = 0;
int error = 0;
va_list list;
size_t sz = 0;
int len = 0;
va_start( list, format);
len = vsnprintf( text, sz, format, list);
va_end ( list );
{
text = calloc( sizeof(char), len+2 );
if(!text)
{
fprintf(stderr, "Could not allocate 256 byte of memory.\n");
return 1;
}
va_start( list, format);
len = vsnprintf( text, len+1, format, list);
va_end ( list );
}
if(text)
fprintf( stderr, "%s\n", text );
lcm2Free_m( text );
return error;
}
{
if(message_func)
lcm2msg_p = message_func;
else
return 1;
}
{
}