/** @brief create_profile.white_point_adjust.bradford * * The profile will be generated in many different shades, which will explode * conversion cache. */ int oyProfileAddWhitePointEffect( oyProfile_s * monitor_profile, oyOptions_s ** module_options ) { oyProfile_s * wtpt = NULL; double src_XYZ[3] = {0.0, 0.0, 0.0}, dst_XYZ[3] = {0.0, 0.0, 0.0}, scale = 1.0; int error = oyProfile_GetWhitePoint( monitor_profile, src_XYZ ); int32_t display_white_point = oyGetBehaviour( oyBEHAVIOUR_DISPLAY_WHITE_POINT ); oyOptions_s * result_opts = NULL, * opts = NULL; const char * desc = NULL; if(*module_options) { const char * value = oyOptions_FindString( *module_options, "display_white_point", 0 ); if(value) { int c = atoi( value ); if(c >= 0) display_white_point = c; } } if(!display_white_point) return 0; if(!error) error = oyGetDisplayWhitePoint( display_white_point, dst_XYZ ); if(isnan(dst_XYZ[0])) error = 1; if(error) { oyMessageFunc_p( oyMSG_WARN,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "automatic display_white_point: not readable", OY_DBG_ARGS_); return error; } desc = oyProfile_GetText( monitor_profile, oyNAME_DESCRIPTION ); error = oyOptions_SetFromString( &opts, "//" OY_TYPE_STD "/src_name", desc, OY_CREATE_NEW ); if(error) return error; DBG_S_( oyPrintTime() ); { int current = -1, choices = 0; const char ** choices_string_list = NULL; uint32_t flags = 0; #ifdef HAVE_LOCALE_H char * old_loc = strdup(setlocale(LC_ALL,NULL)); setlocale(LC_ALL,"C"); #endif error = oyOptionChoicesGet2( oyWIDGET_DISPLAY_WHITE_POINT, flags, oyNAME_NAME, &choices, &choices_string_list, ¤t ); if(error > 0) oyMessageFunc_p( oyMSG_WARN,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "oyOptionChoicesGet2 failed %d", OY_DBG_ARGS_, error); #ifdef HAVE_LOCALE_H setlocale(LC_ALL,old_loc); if(old_loc) { free(old_loc); } old_loc = NULL; #endif if(current > 0 && current < choices && choices_string_list) { if(current == 1) /* automatic */ { double temperature = oyGetTemperature(-1); char k[12]; sprintf(k, "%dK", (int)temperature); oyOptions_SetFromString( &opts, "//" OY_TYPE_STD "/illu_name", k, OY_CREATE_NEW ); if(temperature <= 0.1) oyMessageFunc_p( oyMSG_WARN,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "automatic display_white_point: [%g %g %g] %s", OY_DBG_ARGS_, dst_XYZ[0], dst_XYZ[1], dst_XYZ[2], k); } else oyOptions_SetFromString( &opts, "//" OY_TYPE_STD "/illu_name", choices_string_list[current], OY_CREATE_NEW ); if(oy_debug) oyMessageFunc_p( oyMSG_DBG,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "illu_name: %s", OY_DBG_ARGS_, oyOptions_FindString( opts, "illu_name", 0) ); } oyOptionChoicesFree( oyWIDGET_DISPLAY_WHITE_POINT, &choices_string_list, choices ); } if(oy_debug) oyMessageFunc_p( oyMSG_WARN, NULL, OY_DBG_FORMAT_ "src_name: %s -> illu_name: %s", OY_DBG_ARGS_, oyOptions_FindString(opts, "src_name", 0), oyOptions_FindString(opts, "illu_name", 0) ); oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/src_iccXYZ", src_XYZ[0], 0, OY_CREATE_NEW ); oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/src_iccXYZ", src_XYZ[1], 1, OY_CREATE_NEW ); oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/src_iccXYZ", src_XYZ[2], 2, OY_CREATE_NEW ); oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/illu_iccXYZ", dst_XYZ[0], 0, OY_CREATE_NEW ); oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/illu_iccXYZ", dst_XYZ[1], 1, OY_CREATE_NEW ); oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/illu_iccXYZ", dst_XYZ[2], 2, OY_CREATE_NEW ); /* cache the display white point abstract profile */ error = oyOptions_Handle( "//" OY_TYPE_STD "/create_profile.white_point_adjust.bradford", opts, "create_profile.white_point_adjust.bradford.file_name", &result_opts ); DBG_S_( oyPrintTime() ); if(error > 0) oyMessageFunc_p( oyMSG_WARN,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "oyOptions_Handle(white_point_adjust.bradford.file_name) failed %d", OY_DBG_ARGS_, error); /* write cache profile for slightly better speed and useful for debugging */ if(error == 0) { const char * file_name = oyOptions_FindString( result_opts, "file_name", 0 ); char * cache_path = oyGetInstallPath( oyPATH_CACHE, oySCOPE_USER, oyAllocateFunc_ ), *t; if(strstr( cache_path, "device_link") != NULL) { t = strstr( cache_path, "device_link"); t[0] = '\000'; oyStringAddPrintf( &cache_path, 0,0, "white_point_adjust/%s.icc", file_name ); } if(oyIsFile_(cache_path)) { if(oy_debug) oyMessageFunc_p( oyMSG_DBG,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "found file_name: %s -> %s\n", OY_DBG_ARGS_, file_name, cache_path ); wtpt = oyProfile_FromFile( cache_path, 0,0 ); DBG_S_( oyPrintTime() ); } } if(!error && wtpt) { oyOptions_MoveInStruct( module_options, OY_STD "/icc_color/display.icc_profile.abstract.white_point.automatic.oy-monitor", (oyStruct_s**) &wtpt, OY_CREATE_NEW ); oyOptions_Release( &result_opts ); oyOptions_Release( &opts ); return error; } if(!error && !wtpt) { /* detect scaling factor * - convert monitor device RGB to XYZ, * - apply white point adaption matrix, * - convert back to monitor device RGB and * - again to XYZ to detect clipping and * - use that information for scaling inside the white point effect profile */ oyProfile_s * xyz_profile = oyProfile_FromStd( oyASSUMED_XYZ, 0, 0 ); float rgb[9] = {1,0,0, 0,1,0, 0,0,1}, xyz[9] = {0,0,0, 0,0,0, 0,0,0}; oyDATATYPE_e buf_type = oyFLOAT; oyConversion_s * cc_moni2xyz = oyConversion_CreateBasicPixelsFromBuffers( monitor_profile, rgb, oyDataType_m(buf_type), xyz_profile, xyz, oyDataType_m(buf_type), 0, 3 ); oyConversion_s * cc_xyz2moni = oyConversion_CreateBasicPixelsFromBuffers( xyz_profile, xyz, oyDataType_m(buf_type), monitor_profile, rgb, oyDataType_m(buf_type), 0, 3 ); oyMAT3 wtpt_adapt; oyCIEXYZ srcWtpt = {src_XYZ[0], src_XYZ[1], src_XYZ[2]}, dstIllu = {dst_XYZ[0], dst_XYZ[1], dst_XYZ[2]}; error = !oyAdaptationMatrix( &wtpt_adapt, NULL, &srcWtpt, &dstIllu ); int i,j; for(j = 0; j < 100; ++j) { oyConversion_RunPixels( cc_moni2xyz, 0 ); if(oy_debug) oyMessageFunc_p( oyMSG_DBG,(oyStruct_s*)cc_moni2xyz, OY_DBG_FORMAT_ "rgb->xyz:\nR[%g %g %g] G[%g %g %g] B[%g %g %g]", OY_DBG_ARGS_, xyz[0], xyz[1], xyz[2], xyz[3], xyz[4], xyz[5], xyz[6], xyz[7], xyz[8]); oyVEC3 rXYZ, srcXYZ[3] = { {{xyz[0], xyz[1], xyz[2]}}, {{xyz[3], xyz[4], xyz[5]}}, {{xyz[6], xyz[7], xyz[8]}} }; oyMAT3 wtpt_adapt_scaled, scale_mat = {{ {{scale,0,0}}, {{0,scale,0}}, {{0,0,scale}} }}; oyMAT3per( &wtpt_adapt_scaled, &wtpt_adapt, &scale_mat ); oyMAT3eval( &rXYZ, &wtpt_adapt_scaled, &srcXYZ[0] ); for(i = 0; i < 3; ++i) xyz[0+i] = rXYZ.n[i]; oyMAT3eval( &rXYZ, &wtpt_adapt_scaled, &srcXYZ[1] ); for(i = 0; i < 3; ++i) xyz[3+i] = rXYZ.n[i]; oyMAT3eval( &rXYZ, &wtpt_adapt_scaled, &srcXYZ[2] ); for(i = 0; i < 3; ++i) xyz[6+i] = rXYZ.n[i]; if(oy_debug) oyMessageFunc_p( oyMSG_DBG,(oyStruct_s*)cc_moni2xyz, OY_DBG_FORMAT_ "srcWtpt->Illu:\nR[%g %g %g] G[%g %g %g] B[%g %g %g]", OY_DBG_ARGS_, xyz[0], xyz[1], xyz[2], xyz[3], xyz[4], xyz[5], xyz[6], xyz[7], xyz[8]); oyConversion_RunPixels( cc_xyz2moni, 0 ); if(oy_debug) oyMessageFunc_p( oyMSG_WARN,(oyStruct_s*)cc_moni2xyz, OY_DBG_FORMAT_ "xyz->rgb:\nR[%g %g %g] G[%g %g %g] B[%g %g %g] %g", OY_DBG_ARGS_, rgb[0], rgb[1], rgb[2], rgb[3], rgb[4], rgb[5], rgb[6], rgb[7], rgb[8], scale); if(rgb[0+0] < 0.99 && rgb[3+1] < 0.99 && rgb[6+2] < 0.99) { if(scale < 1.00) scale += 0.01; break; } scale -= 0.01; } } oyMessageFunc_p( /*error ?*/ oyMSG_WARN/*:oyMSG_DBG*/,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "%s display_white_point: %d [%g %g %g] -> [%g %g %g] * %g %d", OY_DBG_ARGS_, desc, display_white_point, src_XYZ[0], src_XYZ[1], src_XYZ[2], dst_XYZ[0], dst_XYZ[1], dst_XYZ[2], scale, error); if(error > 0) return error; /* write cache profile for slightly better speed and useful for debugging */ if(error == 0) { const char * file_name = oyOptions_FindString( result_opts, "file_name", 0 ); char * cache_path = oyGetInstallPath( oyPATH_CACHE, oySCOPE_USER, oyAllocateFunc_ ), *t; if(strstr( cache_path, "device_link") != NULL) { t = strstr( cache_path, "device_link"); t[0] = '\000'; oyStringAddPrintf( &cache_path, 0,0, "white_point_adjust/%s.icc", file_name ); } { error = oyOptions_SetFromDouble( &opts, "//" OY_TYPE_STD "/scale", scale, 0, OY_CREATE_NEW ); if(oy_debug) oyMessageFunc_p( oyMSG_DBG,(oyStruct_s*)monitor_profile, OY_DBG_FORMAT_ "creating file_name: %s -> %s\n", OY_DBG_ARGS_, file_name, cache_path ); error = oyOptions_Handle( "//" OY_TYPE_STD "/create_profile.white_point_adjust.bradford", opts, "create_profile.white_point_adjust.bradford", &result_opts ); wtpt = (oyProfile_s*) oyOptions_GetType( result_opts, -1, "icc_profile", oyOBJECT_PROFILE_S ); error = !wtpt; if(!error) oyProfile_ToFile_( (oyProfile_s_*) wtpt, cache_path ); DBG_S_( oyPrintTime() ); } } error = !wtpt; if(error == 0) oyOptions_MoveInStruct( module_options, OY_STD "/icc_color/display.icc_profile.abstract.white_point.automatic.oy-monitor", (oyStruct_s**) &wtpt, OY_CREATE_NEW ); oyOptions_Release( &result_opts ); oyOptions_Release( &opts ); return error; }
oyConversion_s * oyConversion_FromImageForDisplay_ ( oyImage_s * image_in, oyImage_s * image_out, oyFilterNode_s ** cc_node, uint32_t flags, oyDATATYPE_e data_type, oyOptions_s * cc_options, oyObject_s obj ) { oyFilterNode_s * in = 0, * out = 0, * icc = 0; int error = 0; oyConversion_s * conversion = 0; oyOptions_s * options = 0; oyOption_s * option = 0; const char * sv = 0; double scale = 0; if(!image_in || !image_out) return NULL; /* start with an empty conversion object */ conversion = oyConversion_New( obj ); /* create a filter node */ in = oyFilterNode_NewWith( "//" OY_TYPE_STD "/root", 0, obj ); /* set the above filter node as the input */ oyConversion_Set( conversion, in, 0 ); /* set the image buffer */ oyFilterNode_SetData( in, (oyStruct_s*)image_in, 0, 0 ); /* add a scale node */ out = oyFilterNode_NewWith( "//" OY_TYPE_STD "/scale", 0, obj ); options = oyFilterNode_GetOptions( out, OY_SELECT_FILTER ); /* scale factor from DB */ option = oyOption_FromRegistration( OY_INTERNAL "/scale/scale", 0 ); error = oyOption_SetFromText( option, 0, 0 ); error = oyOption_SetValueFromDB( option ); scale = 1.0; if(!error) { sv = oyOption_GetValueString( option, 0 ); if(sv) scale = strtod( sv, 0 ); } oyOption_Release( &option ); error = oyOptions_SetFromDouble( &options, OY_INTERNAL "/scale/scale", scale, 0, OY_CREATE_NEW ); oyOptions_Release( &options ); /* append the node */ error = oyFilterNode_Connect( in, "//" OY_TYPE_STD "/data", out, "//" OY_TYPE_STD "/data", 0 ); if(error > 0) fprintf( stderr, "could not add filter: %s\n", "//" OY_TYPE_STD "/scale" ); in = out; /* add a expose node */ out = oyFilterNode_NewWith( "//" OY_TYPE_STD "/expose", 0, obj ); options = oyFilterNode_GetOptions( out, OY_SELECT_FILTER ); /* expose factor */ error = oyOptions_SetFromDouble( &options, "//" OY_TYPE_STD "/expose/expose", 1.0, 0, OY_CREATE_NEW ); oyOptions_Release( &options ); /* append the node */ error = oyFilterNode_Connect( in, "//" OY_TYPE_STD "/data", out, "//" OY_TYPE_STD "/data", 0 ); if(error > 0) fprintf( stderr, "could not add filter: %s\n", "//" OY_TYPE_STD "/expose" ); in = out; /* add a channel node */ out = oyFilterNode_NewWith( "//" OY_TYPE_STD "/channel", 0, obj ); options = oyFilterNode_GetOptions( out, OY_SELECT_FILTER ); /* channel option*/ error = oyOptions_SetFromText( &options, "//" OY_TYPE_STD "/channel/channel", "", OY_CREATE_NEW ); oyOptions_Release( &options ); /* append the node */ error = oyFilterNode_Connect( in, "//" OY_TYPE_STD "/data", out, "//" OY_TYPE_STD "/data", 0 ); if(error > 0) fprintf( stderr, "could not add filter: %s\n", "//" OY_TYPE_STD "/channel" ); in = out; /* create a new filter node */ { icc = out = oyFilterNode_FromOptions( OY_CMM_STD, "//" OY_TYPE_STD "/icc_color", cc_options, NULL ); /* append the new to the previous one */ error = oyFilterNode_Connect( in, "//" OY_TYPE_STD "/data", out, "//" OY_TYPE_STD "/data", 0 ); if(error > 0) fprintf( stderr, "could not add filter: %s\n", OY_CMM_STD ); /* Set the image to the first/only socket of the filter node. * oyFilterNode_Connect() has now no chance to copy it it the other nodes. * We rely on resolving the image later. */ error = oyFilterNode_SetData( out, (oyStruct_s*)image_out, 0, 0 ); if(error != 0) fprintf( stderr, "could not add data\n" ); } /* swap in and out */ if(out) in = out; /* create a node for preparing the image for displaying */ { out = oyFilterNode_NewWith( "//" OY_TYPE_STD "/display", 0, obj ); options = oyFilterNode_GetOptions( out, OY_SELECT_FILTER ); /* data type for display */ error = oyOptions_SetFromInt( &options, "//" OY_TYPE_STD "/display/datatype", data_type, 0, OY_CREATE_NEW ); /* alpha might be support once by FLTK? */ error = oyOptions_SetFromInt( &options, "//" OY_TYPE_STD "/display/preserve_alpha", 1, 0, OY_CREATE_NEW ); oyOptions_Release( &options ); /* append the node */ error = oyFilterNode_Connect( in, "//" OY_TYPE_STD "/data", out, "//" OY_TYPE_STD "/data", 0 ); if(error > 0) fprintf( stderr, "could not add filter: %s\n", "//" OY_TYPE_STD "/display" ); oyFilterNode_SetData( out, (oyStruct_s*)image_out, 0, 0 ); in = out; } /* add a closing node */ out = oyFilterNode_NewWith( "//" OY_TYPE_STD "/output", 0, obj ); error = oyFilterNode_Connect( in, "//" OY_TYPE_STD "/data", out, "//" OY_TYPE_STD "/data", 0 ); if(error > 0) fprintf( stderr, "could not add filter: %s\n", "//" OY_TYPE_STD "/output" ); /* set the output node of the conversion */ oyConversion_Set( conversion, 0, out ); /* apply policies */ /*error = oyOptions_SetFromText( &options, "//" OY_TYPE_STD "//verbose", "true", OY_CREATE_NEW );*/ oyConversion_Correct( conversion, "//" OY_TYPE_STD "/icc_color", flags, options ); oyOptions_Release( &options ); if(cc_node) *cc_node = icc; return conversion; }