IccColorProfile *LcmsColorProfileContainer::createFromLcmsProfile(const cmsHPROFILE profile) { IccColorProfile *iccprofile = new IccColorProfile(lcmsProfileToByteArray(profile)); cmsCloseProfile(profile); return iccprofile; }
void gui_init(struct dt_iop_module_t *self) { self->gui_data = malloc(sizeof(dt_iop_colorout_gui_data_t)); memset(self->gui_data,0,sizeof(dt_iop_colorout_gui_data_t)); dt_iop_colorout_gui_data_t *g = (dt_iop_colorout_gui_data_t *)self->gui_data; g->profiles = NULL; dt_iop_color_profile_t *prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "sRGB", sizeof(prof->filename)); g_strlcpy(prof->name, "sRGB", sizeof(prof->name)); int pos; int display_pos; prof->pos = 0; prof->display_pos = 0; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "adobergb", sizeof(prof->filename)); g_strlcpy(prof->name, "adobergb", sizeof(prof->name)); prof->pos = 1; prof->display_pos = 1; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "X profile", sizeof(prof->filename)); g_strlcpy(prof->name, "X profile", sizeof(prof->name)); prof->pos = -1; prof->display_pos = 2; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "linear_rgb", sizeof(prof->filename)); g_strlcpy(prof->name, "linear_rgb", sizeof(prof->name)); pos = prof->pos = 2; display_pos = prof->display_pos = 3; g->profiles = g_list_append(g->profiles, prof); // read {conf,data}dir/color/out/*.icc char datadir[DT_MAX_PATH_LEN]; char confdir[DT_MAX_PATH_LEN]; char dirname[DT_MAX_PATH_LEN]; char filename[DT_MAX_PATH_LEN]; dt_loc_get_user_config_dir(confdir, DT_MAX_PATH_LEN); dt_loc_get_datadir(datadir, DT_MAX_PATH_LEN); snprintf(dirname, DT_MAX_PATH_LEN, "%s/color/out", confdir); if(!g_file_test(dirname, G_FILE_TEST_IS_DIR)) snprintf(dirname, DT_MAX_PATH_LEN, "%s/color/out", datadir); cmsHPROFILE tmpprof; const gchar *d_name; GDir *dir = g_dir_open(dirname, 0, NULL); if(dir) { while((d_name = g_dir_read_name(dir))) { snprintf(filename, DT_MAX_PATH_LEN, "%s/%s", dirname, d_name); tmpprof = cmsOpenProfileFromFile(filename, "r"); if(tmpprof) { char *lang = getenv("LANG"); if (!lang) lang = "en_US"; dt_iop_color_profile_t *prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); char name[1024]; cmsGetProfileInfoASCII(tmpprof, cmsInfoDescription, lang, lang+3, name, 1024); g_strlcpy(prof->name, name, sizeof(prof->name)); g_strlcpy(prof->filename, d_name, sizeof(prof->filename)); prof->pos = ++pos; prof->display_pos = ++display_pos; cmsCloseProfile(tmpprof); g->profiles = g_list_append(g->profiles, prof); } } g_dir_close(dir); } self->widget = gtk_vbox_new(TRUE, DT_BAUHAUS_SPACE); // TODO: g->cbox1 = dt_bauhaus_combobox_new(self); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox1, TRUE, TRUE, 0); dt_bauhaus_widget_set_label(g->cbox1, _("output intent")); dt_bauhaus_combobox_add(g->cbox1, _("perceptual")); dt_bauhaus_combobox_add(g->cbox1, _("relative colorimetric")); dt_bauhaus_combobox_add(g->cbox1, C_("rendering intent", "saturation")); dt_bauhaus_combobox_add(g->cbox1, _("absolute colorimetric")); g->cbox4 = dt_bauhaus_combobox_new(self); dt_bauhaus_widget_set_label(g->cbox4, _("display intent")); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox4, TRUE, TRUE, 0); dt_bauhaus_combobox_add(g->cbox4, _("perceptual")); dt_bauhaus_combobox_add(g->cbox4, _("relative colorimetric")); dt_bauhaus_combobox_add(g->cbox4, C_("rendering intent", "saturation")); dt_bauhaus_combobox_add(g->cbox4, _("absolute colorimetric")); g->cbox2 = dt_bauhaus_combobox_new(self); g->cbox3 = dt_bauhaus_combobox_new(self); g->cbox5 = dt_bauhaus_combobox_new(self); dt_bauhaus_widget_set_label(g->cbox2, _("output profile")); dt_bauhaus_widget_set_label(g->cbox5, _("softproof profile")); dt_bauhaus_widget_set_label(g->cbox3, _("display profile")); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox2, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox5, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox3, TRUE, TRUE, 0); GList *l = g->profiles; while(l) { dt_iop_color_profile_t *prof = (dt_iop_color_profile_t *)l->data; if(!strcmp(prof->name, "X profile")) { // the system display profile is only suitable for display purposes dt_bauhaus_combobox_add(g->cbox3, _("system display profile")); } else if(!strcmp(prof->name, "linear_rgb")) { dt_bauhaus_combobox_add(g->cbox2, _("linear RGB")); dt_bauhaus_combobox_add(g->cbox3, _("linear RGB")); dt_bauhaus_combobox_add(g->cbox5, _("linear RGB")); } else if(!strcmp(prof->name, "sRGB")) { dt_bauhaus_combobox_add(g->cbox2, _("sRGB (web-safe)")); dt_bauhaus_combobox_add(g->cbox3, _("sRGB (web-safe)")); dt_bauhaus_combobox_add(g->cbox5, _("sRGB (web-safe)")); } else if(!strcmp(prof->name, "adobergb")) { dt_bauhaus_combobox_add(g->cbox2, _("Adobe RGB")); dt_bauhaus_combobox_add(g->cbox3, _("Adobe RGB")); dt_bauhaus_combobox_add(g->cbox5, _("Adobe RGB")); } else { dt_bauhaus_combobox_add(g->cbox2, prof->name); dt_bauhaus_combobox_add(g->cbox3, prof->name); dt_bauhaus_combobox_add(g->cbox5, prof->name); } l = g_list_next(l); } char tooltip[1024]; g_object_set(G_OBJECT(g->cbox1), "tooltip-text", _("rendering intent"), (char *)NULL); snprintf(tooltip, 1024, _("icc profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(g->cbox2), "tooltip-text", tooltip, (char *)NULL); snprintf(tooltip, 1024, _("display icc profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(g->cbox3), "tooltip-text", tooltip, (char *)NULL); snprintf(tooltip, 1024, _("softproof icc profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(g->cbox5), "tooltip-text", tooltip, (char *)NULL); g_signal_connect (G_OBJECT (g->cbox1), "value-changed", G_CALLBACK (intent_changed), (gpointer)self); g_signal_connect (G_OBJECT (g->cbox4), "value-changed", G_CALLBACK (display_intent_changed), (gpointer)self); g_signal_connect (G_OBJECT (g->cbox2), "value-changed", G_CALLBACK (output_profile_changed), (gpointer)self); g_signal_connect (G_OBJECT (g->cbox3), "value-changed", G_CALLBACK (display_profile_changed), (gpointer)self); g_signal_connect (G_OBJECT (g->cbox5), "value-changed", G_CALLBACK (softproof_profile_changed), (gpointer)self); // reload the profiles when the display profile changed! dt_control_signal_connect(darktable.signals, DT_SIGNAL_CONTROL_PROFILE_CHANGED, G_CALLBACK(_signal_profile_changed), self); }
// This is the entry for black-plane preserving, which are non-ICC static cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, cmsUInt32Number nProfiles, cmsUInt32Number TheIntents[], cmsHPROFILE hProfiles[], cmsBool BPC[], cmsFloat64Number AdaptationStates[], cmsUInt32Number dwFlags) { PreserveKPlaneParams bp; cmsPipeline* Result = NULL; cmsUInt32Number ICCIntents[256]; cmsStage* CLUT; cmsUInt32Number i, nGridPoints; cmsHPROFILE hLab; // Sanity check if (nProfiles < 1 || nProfiles > 255) return NULL; // Translate black-preserving intents to ICC ones for (i=0; i < nProfiles; i++) ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); // Check for non-cmyk profiles if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); // Allocate an empty LUT for holding the result Result = cmsPipelineAlloc(ContextID, 4, 4); if (Result == NULL) return NULL; memset(&bp, 0, sizeof(bp)); // We need the input LUT of the last profile, assuming this one is responsible of // black generation. This LUT will be seached in inverse order. bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); if (bp.LabK2cmyk == NULL) goto Cleanup; // Get total area coverage (in 0..1 domain) bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; if (bp.MaxTAC <= 0) goto Cleanup; // Create a LUT holding normal ICC transform bp.cmyk2cmyk = DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); if (bp.cmyk2cmyk == NULL) goto Cleanup; // Now the tone curve bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); if (bp.KTone == NULL) goto Cleanup; // To measure the output, Last profile to Lab hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); if ( bp.hProofOutput == NULL) goto Cleanup; // Same as anterior, but lab in the 0..1 range bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); if (bp.cmyk2Lab == NULL) goto Cleanup; cmsCloseProfile(hLab); // Error estimation (for debug only) bp.MaxError = 0; // How many gridpoints are we going to use? nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); if (CLUT == NULL) goto Cleanup; if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) goto Cleanup; cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); Cleanup: if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); if (bp.KTone) cmsFreeToneCurve(bp.KTone); if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); return Result; }
// Creates all needed color transforms static cmsBool OpenTransforms(void) { cmsHPROFILE hInput, hOutput, hProof; cmsUInt32Number dwIn, dwOut, dwFlags; cmsNAMEDCOLORLIST* List; int i; // We don't need cache dwFlags = cmsFLAGS_NOCACHE; if (lIsDeviceLink) { hInput = OpenStockProfile(0, cInProf); if (hInput == NULL) return FALSE; hOutput = NULL; hProof = NULL; if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) { OutputColorSpace = cmsGetColorSpace(hInput); InputColorSpace = cmsGetPCS(hInput); } else { InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetPCS(hInput); } // Read colorant tables if present if (cmsIsTag(hInput, cmsSigColorantTableTag)) { List = cmsReadTag(hInput, cmsSigColorantTableTag); InputColorant = cmsDupNamedColorList(List); InputRange = 1; } else InputColorant = ComponentNames(InputColorSpace, TRUE); if (cmsIsTag(hInput, cmsSigColorantTableOutTag)){ List = cmsReadTag(hInput, cmsSigColorantTableOutTag); OutputColorant = cmsDupNamedColorList(List); OutputRange = 1; } else OutputColorant = ComponentNames(OutputColorSpace, FALSE); } else { hInput = OpenStockProfile(0, cInProf); if (hInput == NULL) return FALSE; hOutput = OpenStockProfile(0, cOutProf); if (hOutput == NULL) return FALSE; hProof = NULL; if (cmsGetDeviceClass(hInput) == cmsSigLinkClass || cmsGetDeviceClass(hOutput) == cmsSigLinkClass) FatalError("Use %cl flag for devicelink profiles!\n", SW); InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetColorSpace(hOutput); // Read colorant tables if present if (cmsIsTag(hInput, cmsSigColorantTableTag)) { List = cmsReadTag(hInput, cmsSigColorantTableTag); InputColorant = cmsDupNamedColorList(List); if (cmsNamedColorCount(InputColorant) <= 3) SetRange(255, TRUE); else SetRange(1, TRUE); // Inks are already divided by 100 in the formatter } else InputColorant = ComponentNames(InputColorSpace, TRUE); if (cmsIsTag(hOutput, cmsSigColorantTableTag)){ List = cmsReadTag(hOutput, cmsSigColorantTableTag); OutputColorant = cmsDupNamedColorList(List); if (cmsNamedColorCount(OutputColorant) <= 3) SetRange(255, FALSE); else SetRange(1, FALSE); // Inks are already divided by 100 in the formatter } else OutputColorant = ComponentNames(OutputColorSpace, FALSE); if (cProofing != NULL) { hProof = OpenStockProfile(0, cProofing); if (hProof == NULL) return FALSE; dwFlags |= cmsFLAGS_SOFTPROOFING; } } // Print information on profiles if (Verbose > 2) { printf("Profile:\n"); PrintProfileInformation(hInput); if (hOutput) { printf("Output profile:\n"); PrintProfileInformation(hOutput); } if (hProof != NULL) { printf("Proofing profile:\n"); PrintProfileInformation(hProof); } } // Input is always in floating point dwIn = cmsFormatterForColorspaceOfProfile(hInput, 0, TRUE); if (lIsDeviceLink) { dwOut = cmsFormatterForPCSOfProfile(hInput, lIsFloat ? 0 : 2, lIsFloat); } else { // 16 bits or floating point (only on output) dwOut = cmsFormatterForColorspaceOfProfile(hOutput, lIsFloat ? 0 : 2, lIsFloat); } // For named color, there is a specialized formatter if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) { dwIn = TYPE_NAMED_COLOR_INDEX; InputNamedColor = TRUE; } // Precision mode switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; case 1: break; default: FatalError("Unknown precalculation mode '%d'", PrecalcMode); } if (BlackPointCompensation) dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; if (GamutCheck) { cmsUInt16Number Alarm[cmsMAXCHANNELS]; if (hProof == NULL) FatalError("I need proofing profile -p for gamut checking!"); for (i=0; i < cmsMAXCHANNELS; i++) Alarm[i] = 0xFFFF; cmsSetAlarmCodes(Alarm); dwFlags |= cmsFLAGS_GAMUTCHECK; } // The main transform hTrans = cmsCreateProofingTransform(hInput, dwIn, hOutput, dwOut, hProof, Intent, ProofingIntent, dwFlags); if (hProof) cmsCloseProfile(hProof); if (hTrans == NULL) return FALSE; // PCS Dump if requested hTransXYZ = NULL; hTransLab = NULL; if (hOutput && Verbose > 1) { cmsHPROFILE hXYZ = cmsCreateXYZProfile(); cmsHPROFILE hLab = cmsCreateLab4Profile(NULL); hTransXYZ = cmsCreateTransform(hInput, dwIn, hXYZ, lIsFloat ? TYPE_XYZ_DBL : TYPE_XYZ_16, Intent, cmsFLAGS_NOCACHE); if (hTransXYZ == NULL) return FALSE; hTransLab = cmsCreateTransform(hInput, dwIn, hLab, lIsFloat? TYPE_Lab_DBL : TYPE_Lab_16, Intent, cmsFLAGS_NOCACHE); if (hTransLab == NULL) return FALSE; cmsCloseProfile(hXYZ); cmsCloseProfile(hLab); } if (hInput) cmsCloseProfile(hInput); if (hOutput) cmsCloseProfile(hOutput); return TRUE; }
void gui_init (dt_lib_module_t *self) { dt_lib_export_t *d = (dt_lib_export_t *)malloc(sizeof(dt_lib_export_t)); self->data = (void *)d; self->widget = gtk_table_new(8, 2, FALSE); gtk_table_set_row_spacings(GTK_TABLE(self->widget), 5); GtkWidget *label; label = dtgtk_label_new(_("target storage"), DARKTABLE_LABEL_TAB | DARKTABLE_LABEL_ALIGN_RIGHT); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 2, 0, 1, GTK_FILL|GTK_EXPAND, 0, 0, 0); d->storage = GTK_COMBO_BOX(gtk_combo_box_new_text()); GList *it = darktable.imageio->plugins_storage; while(it) { dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data; gtk_combo_box_append_text(d->storage, module->name(module)); it = g_list_next(it); } dt_control_signal_connect(darktable.signals,DT_SIGNAL_IMAGEIO_STORAGE_CHANGE,G_CALLBACK(on_storage_list_changed),self); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->storage), 0, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect (G_OBJECT (d->storage), "changed", G_CALLBACK (storage_changed), (gpointer)d); d->storage_box = GTK_CONTAINER(gtk_alignment_new(1.0, 1.0, 1.0, 1.0)); gtk_alignment_set_padding(GTK_ALIGNMENT(d->storage_box), 0, 0, 0, 0); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->storage_box), 0, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0); label = dtgtk_label_new(_("file format"), DARKTABLE_LABEL_TAB | DARKTABLE_LABEL_ALIGN_RIGHT); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_set_row_spacing(GTK_TABLE(self->widget), 2, 20); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 2, 3, 4, GTK_EXPAND|GTK_FILL, 0, 0, 0); d->format = GTK_COMBO_BOX(gtk_combo_box_new_text()); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->format), 0, 2, 4, 5, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect (G_OBJECT (d->format), "changed", G_CALLBACK (format_changed), (gpointer)d); d->format_box = GTK_CONTAINER(gtk_alignment_new(1.0, 1.0, 1.0, 1.0)); gtk_alignment_set_padding(GTK_ALIGNMENT(d->format_box), 0, 0, 0, 0); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->format_box), 0, 2, 5, 6, GTK_EXPAND|GTK_FILL, 0, 0, 0); label = dtgtk_label_new(_("global options"), DARKTABLE_LABEL_TAB | DARKTABLE_LABEL_ALIGN_RIGHT); gtk_table_set_row_spacing(GTK_TABLE(self->widget), 5, 20); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 2, 6, 7, GTK_EXPAND|GTK_FILL, 0, 0, 0); d->width = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(0, 10000, 1)); g_object_set(G_OBJECT(d->width), "tooltip-text", _("maximum output width\nset to 0 for no scaling"), (char *)NULL); d->height = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(0, 10000, 1)); g_object_set(G_OBJECT(d->height), "tooltip-text", _("maximum output height\nset to 0 for no scaling"), (char *)NULL); dt_gui_key_accel_block_on_focus_connect (GTK_WIDGET (d->width)); dt_gui_key_accel_block_on_focus_connect (GTK_WIDGET (d->height)); /* gtk_widget_add_events(GTK_WIDGET(d->width), GDK_FOCUS_CHANGE_MASK); g_signal_connect (G_OBJECT (d->width), "focus-in-event", G_CALLBACK(focus_in), NULL); g_signal_connect (G_OBJECT (d->width), "focus-out-event", G_CALLBACK(focus_out), NULL); gtk_widget_add_events(GTK_WIDGET(d->height), GDK_FOCUS_CHANGE_MASK); g_signal_connect (G_OBJECT (d->height), "focus-in-event", G_CALLBACK(focus_in), NULL); g_signal_connect (G_OBJECT (d->height), "focus-out-event", G_CALLBACK(focus_out), NULL); */ label = gtk_label_new(_("max size")); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 1, 7, 8, GTK_EXPAND|GTK_FILL, 0, 0, 0); GtkBox *hbox = GTK_BOX(gtk_hbox_new(FALSE, 5)); gtk_box_pack_start(hbox, GTK_WIDGET(d->width), TRUE, TRUE, 0); gtk_box_pack_start(hbox, gtk_label_new(_("x")), FALSE, FALSE, 0); gtk_box_pack_start(hbox, GTK_WIDGET(d->height), TRUE, TRUE, 0); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(hbox), 1, 2, 7, 8, GTK_EXPAND|GTK_FILL, 0, 0, 0); label = gtk_label_new(_("intent")); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 1, 8, 9, GTK_EXPAND|GTK_FILL, 0, 0, 0); d->intent = GTK_COMBO_BOX(gtk_combo_box_new_text()); gtk_combo_box_append_text(d->intent, _("image settings")); gtk_combo_box_append_text(d->intent, _("perceptual")); gtk_combo_box_append_text(d->intent, _("relative colorimetric")); gtk_combo_box_append_text(d->intent, C_("rendering intent", "saturation")); gtk_combo_box_append_text(d->intent, _("absolute colorimetric")); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->intent), 1, 2, 8, 9, GTK_EXPAND|GTK_FILL, 0, 0, 0); // Add profile combo d->profiles = NULL; dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); g_strlcpy(prof->filename, "sRGB", sizeof(prof->filename)); g_strlcpy(prof->name, _("sRGB (web-safe)"), sizeof(prof->name)); int pos; prof->pos = 1; d->profiles = g_list_append(d->profiles, prof); prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); g_strlcpy(prof->filename, "adobergb", sizeof(prof->filename)); g_strlcpy(prof->name, _("Adobe RGB"), sizeof(prof->name)); prof->pos = 2; d->profiles = g_list_append(d->profiles, prof); prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); g_strlcpy(prof->filename, "X profile", sizeof(prof->filename)); g_strlcpy(prof->name, "X profile", sizeof(prof->name)); prof->pos = 3; d->profiles = g_list_append(d->profiles, prof); prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); g_strlcpy(prof->filename, "linear_rgb", sizeof(prof->filename)); g_strlcpy(prof->name, _("linear RGB"), sizeof(prof->name)); pos = prof->pos = 4; d->profiles = g_list_append(d->profiles, prof); // read datadir/color/out/*.icc char datadir[DT_MAX_PATH_LEN]; char confdir[DT_MAX_PATH_LEN]; char dirname[DT_MAX_PATH_LEN]; char filename[DT_MAX_PATH_LEN]; dt_loc_get_user_config_dir(confdir, DT_MAX_PATH_LEN); dt_loc_get_datadir(datadir, DT_MAX_PATH_LEN); cmsHPROFILE tmpprof; const gchar *d_name; snprintf(dirname, DT_MAX_PATH_LEN, "%s/color/out", confdir); if(!g_file_test(dirname, G_FILE_TEST_IS_DIR)) snprintf(dirname, DT_MAX_PATH_LEN, "%s/color/out", datadir); GDir *dir = g_dir_open(dirname, 0, NULL); if(dir) { while((d_name = g_dir_read_name(dir))) { snprintf(filename, DT_MAX_PATH_LEN, "%s/%s", dirname, d_name); tmpprof = cmsOpenProfileFromFile(filename, "r"); if(tmpprof) { char *lang = getenv("LANG"); if (!lang) lang = "en_US"; dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); char name[1024]; cmsGetProfileInfoASCII(tmpprof, cmsInfoDescription, lang, lang+3, name, 1024); g_strlcpy(prof->name, name, sizeof(prof->name)); g_strlcpy(prof->filename, d_name, sizeof(prof->filename)); prof->pos = ++pos; cmsCloseProfile(tmpprof); d->profiles = g_list_append(d->profiles, prof); } } g_dir_close(dir); } GList *l = d->profiles; label = gtk_label_new(_("profile")); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 1, 9, 10, GTK_EXPAND|GTK_FILL, 0, 0, 0); d->profile = GTK_COMBO_BOX(gtk_combo_box_new_text()); dt_ellipsize_combo(d->profile); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->profile), 1, 2, 9, 10, GTK_SHRINK|GTK_EXPAND|GTK_FILL, 0, 0, 0); // gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->profile), 1, 2, 9, 10, GTK_EXPAND|GTK_FILL, 0, 0, 0); gtk_combo_box_append_text(d->profile, _("image settings")); while(l) { dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)l->data; if(!strcmp(prof->name, "X profile")) gtk_combo_box_append_text(d->profile, _("system display profile")); else gtk_combo_box_append_text(d->profile, prof->name); l = g_list_next(l); } gtk_combo_box_set_active(d->profile, 0); char tooltip[1024]; snprintf(tooltip, 1024, _("output icc profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(d->profile), "tooltip-text", tooltip, (char *)NULL); // Add style combo label = gtk_label_new(_("style")); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(self->widget), label, 0, 1, 10, 11, GTK_EXPAND|GTK_FILL, 0, 0, 0); d->style = GTK_COMBO_BOX(gtk_combo_box_new_text()); dt_ellipsize_combo(d->style); gtk_combo_box_append_text(d->style, _("none")); GList *styles = dt_styles_get_list(""); while (styles) { dt_style_t *style=(dt_style_t *)styles->data; gtk_combo_box_append_text(d->style, style->name); styles=g_list_next(styles); } gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(d->style), 1, 2, 10, 11, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_object_set(G_OBJECT(d->style), "tooltip-text", _("temporary style to append while exporting"), (char *)NULL); // Set callback signals g_signal_connect (G_OBJECT (d->intent), "changed", G_CALLBACK (intent_changed), (gpointer)d); g_signal_connect (G_OBJECT (d->profile), "changed", G_CALLBACK (profile_changed), (gpointer)d); g_signal_connect (G_OBJECT (d->style), "changed", G_CALLBACK (style_changed), (gpointer)d); // Export button GtkButton *button = GTK_BUTTON(gtk_button_new_with_label(_("export"))); d->export_button = button; g_object_set(G_OBJECT(button), "tooltip-text", _("export with current settings (ctrl-e)"), (char *)NULL); gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(button), 1, 2, 11, 12, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (export_button_clicked), (gpointer)self); g_signal_connect (G_OBJECT (d->width), "value-changed", G_CALLBACK (width_changed), (gpointer)0); g_signal_connect (G_OBJECT (d->height), "value-changed", G_CALLBACK (height_changed), (gpointer)0); self->gui_reset(self); }
void mnglcms_freeprofile (mng_cmsprof hProf) { cmsCloseProfile (hProf); return; }
static int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; int i, nChannels; cmsUInt32Number OutputFormat; _cmsTRANSFORM* v; cmsPipeline* DeviceLink; cmsHPROFILE Profiles[3]; cmsCIEXYZ BlackPointAdaptedToD50; cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); cmsUInt32Number InFrm = TYPE_Lab_16; int RelativeEncodingIntent; cmsColorSpaceSignature ColorSpace; hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); if (hLab == NULL) return 0; OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); nChannels = T_CHANNELS(OutputFormat); ColorSpace = cmsGetColorSpace(hProfile); // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. RelativeEncodingIntent = Intent; if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; // Use V4 Lab always Profiles[0] = hLab; Profiles[1] = hProfile; xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, Profiles, 2, TYPE_Lab_DBL, OutputFormat, RelativeEncodingIntent, 0); cmsCloseProfile(hLab); if (xform == NULL) { cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); return 0; } // Get a copy of the internal devicelink v = (_cmsTRANSFORM*) xform; DeviceLink = cmsPipelineDup(v ->Lut); if (DeviceLink == NULL) return 0; // We need a CLUT dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); _cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "/ColorRenderingType 1\n"); cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); // Emit headers, etc. EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); EmitXYZ2Lab(m); // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to // zero. This would sacrifice a bit of highlights, but failure to do so would cause // scum dot. Ouch. if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) lFixWhite = FALSE; _cmsIOPrintf(m, "/RenderTable "); WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); _cmsIOPrintf(m, " %d {} bind ", nChannels); for (i=1; i < nChannels; i++) _cmsIOPrintf(m, "dup "); _cmsIOPrintf(m, "]\n"); EmitIntent(m, Intent); _cmsIOPrintf(m, ">>\n"); if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); } cmsPipelineFree(DeviceLink); cmsDeleteTransform(xform); return 1; }
LCMSBOOL dkCmsCloseProfile(cmsHPROFILE hProfile) { return static_cast<LCMSBOOL>( cmsCloseProfile(hProfile) ); }
static GimpPDBStatusType lcms_dialog (GimpColorConfig *config, gint32 image, gboolean apply, LcmsValues *values) { GimpColorProfileComboBox *box; GtkWidget *dialog; GtkWidget *main_vbox; GtkWidget *frame; GtkWidget *label; GtkWidget *combo; cmsHPROFILE src_profile; gchar *name; gboolean success = FALSE; gboolean run; src_profile = lcms_image_get_profile (config, image, NULL); if (src_profile && ! lcms_icc_profile_is_rgb (src_profile)) { g_printerr ("lcms: attached color profile is not for RGB color space " "(skipping)\n"); cmsCloseProfile (src_profile); src_profile = NULL; } if (! src_profile) src_profile = cmsCreate_sRGBProfile (); gimp_ui_init (PLUG_IN_BINARY, FALSE); dialog = gimp_dialog_new (apply ? _("Convert to ICC Color Profile") : _("Assign ICC Color Profile"), PLUG_IN_ROLE, NULL, 0, gimp_standard_help_func, apply ? PLUG_IN_PROC_APPLY : PLUG_IN_PROC_SET, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, apply ? GTK_STOCK_CONVERT : _("_Assign"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gimp_window_set_transient (GTK_WINDOW (dialog)); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); frame = gimp_frame_new (_("Current Color Profile")); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); name = lcms_icc_profile_get_desc (src_profile); if (! name) name = lcms_icc_profile_get_name (src_profile); label = gtk_label_new (name); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_container_add (GTK_CONTAINER (frame), label); gtk_widget_show (label); g_free (name); frame = gimp_frame_new (apply ? _("Convert to") : _("Assign")); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); combo = lcms_icc_combo_box_new (config, NULL); gtk_container_add (GTK_CONTAINER (frame), combo); gtk_widget_show (combo); box = GIMP_COLOR_PROFILE_COMBO_BOX (combo); if (apply) { GtkWidget *vbox; GtkWidget *hbox; GtkWidget *toggle; vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("_Rendering Intent:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); combo = gimp_enum_combo_box_new (GIMP_TYPE_COLOR_RENDERING_INTENT); gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); gtk_widget_show (combo); gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), values->intent, G_CALLBACK (gimp_int_combo_box_get_active), &values->intent); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); toggle = gtk_check_button_new_with_mnemonic (_("_Black Point Compensation")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), values->bpc); gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); gtk_widget_show (toggle); g_signal_connect (toggle, "toggled", G_CALLBACK (gimp_toggle_button_update), &values->bpc); } while ((run = gimp_dialog_run (GIMP_DIALOG (dialog))) == GTK_RESPONSE_OK) { gchar *filename = gimp_color_profile_combo_box_get_active (box); cmsHPROFILE dest_profile; gtk_widget_set_sensitive (dialog, FALSE); if (filename) { dest_profile = lcms_load_profile (filename, NULL); } else { dest_profile = cmsCreate_sRGBProfile (); } if (dest_profile) { if (lcms_icc_profile_is_rgb (dest_profile)) { if (apply) success = lcms_image_apply_profile (image, src_profile, dest_profile, filename, values->intent, values->bpc); else success = lcms_image_set_profile (image, dest_profile, filename, TRUE); } else { gimp_message (_("Destination profile is not for RGB color space.")); } cmsCloseProfile (dest_profile); } if (success) break; else gtk_widget_set_sensitive (dialog, TRUE); } gtk_widget_destroy (dialog); cmsCloseProfile (src_profile); return (run ? (success ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR) : GIMP_PDB_CANCEL); }
static GimpPDBStatusType lcms_icc_apply (GimpColorConfig *config, GimpRunMode run_mode, gint32 image, GFile *file, GimpColorRenderingIntent intent, gboolean bpc, gboolean *dont_ask) { GimpPDBStatusType status = GIMP_PDB_SUCCESS; cmsHPROFILE src_profile = NULL; cmsHPROFILE dest_profile = NULL; GError *error = NULL; g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), GIMP_PDB_CALLING_ERROR); g_return_val_if_fail (image != -1, GIMP_PDB_CALLING_ERROR); if (file) g_object_ref (file); else if (config->rgb_profile) file = g_file_new_for_path (config->rgb_profile); if (file) { GError *error = NULL; dest_profile = gimp_lcms_profile_open_from_file (file, &error); if (! dest_profile) { g_message ("%s", error->message); g_clear_error (&error); return GIMP_PDB_EXECUTION_ERROR; } if (! gimp_lcms_profile_is_rgb (dest_profile)) { g_message (_("Color profile '%s' is not for RGB color space."), gimp_file_get_utf8_name (file)); cmsCloseProfile (dest_profile); g_object_unref (file); return GIMP_PDB_EXECUTION_ERROR; } } src_profile = lcms_image_get_profile (config, image, &error); if (error) { g_message ("%s", error->message); g_clear_error (&error); } if (! src_profile && ! dest_profile) return GIMP_PDB_SUCCESS; if (! src_profile) src_profile = gimp_lcms_create_srgb_profile (); if (! dest_profile) dest_profile = gimp_lcms_create_srgb_profile (); if (gimp_lcms_profile_is_equal (src_profile, dest_profile)) { gchar *src_label = gimp_lcms_profile_get_label (src_profile); gchar *dest_label = gimp_lcms_profile_get_label (dest_profile); cmsCloseProfile (src_profile); cmsCloseProfile (dest_profile); g_printerr ("lcms: skipping conversion because profiles seem to be equal:\n"); g_printerr (" %s\n", src_label); g_printerr (" %s\n", dest_label); g_free (src_label); g_free (dest_label); if (file) g_object_unref (file); return GIMP_PDB_SUCCESS; } if (run_mode == GIMP_RUN_INTERACTIVE && ! lcms_icc_apply_dialog (image, src_profile, dest_profile, dont_ask)) { status = GIMP_PDB_CANCEL; } if (status == GIMP_PDB_SUCCESS && ! lcms_image_apply_profile (image, src_profile, dest_profile, file, intent, bpc)) { status = GIMP_PDB_EXECUTION_ERROR; } cmsCloseProfile (src_profile); cmsCloseProfile (dest_profile); if (file) g_object_unref (file); return status; }
static gboolean lcms_image_set_profile (gint32 image, cmsHPROFILE profile, GFile *file) { g_return_val_if_fail (image != -1, FALSE); if (file) { cmsHPROFILE file_profile; GimpParasite *parasite; guint8 *profile_data; gsize profile_length; GError *error = NULL; /* check that this file is actually an ICC profile */ file_profile = gimp_lcms_profile_open_from_file (file, &error); if (! file_profile) { g_message ("%s", error->message); g_clear_error (&error); return FALSE; } profile_data = gimp_lcms_profile_save_to_data (file_profile, &profile_length, &error); cmsCloseProfile (file_profile); if (! profile_data) { g_message ("%s", error->message); g_clear_error (&error); return FALSE; } gimp_image_undo_group_start (image); parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, profile_length, profile_data); g_free (profile_data); gimp_image_attach_parasite (image, parasite); gimp_parasite_free (parasite); } else { gimp_image_undo_group_start (image); gimp_image_detach_parasite (image, "icc-profile"); } gimp_image_detach_parasite (image, "icc-profile-name"); gimp_image_undo_group_end (image); return TRUE; }
static GtkWidget * lcms_icc_combo_box_new (GimpColorConfig *config, const gchar *filename) { GtkWidget *combo; GtkWidget *dialog; gchar *history; gchar *label; gchar *name; const gchar *rgb_filename = NULL; cmsHPROFILE profile = NULL; dialog = gimp_color_profile_chooser_dialog_new (_("Select destination profile")); history = gimp_personal_rc_file ("profilerc"); combo = gimp_color_profile_combo_box_new (dialog, history); g_free (history); if (config->rgb_profile) { GFile *file = g_file_new_for_path (config->rgb_profile); GError *error = NULL; profile = gimp_lcms_profile_open_from_file (file, &error); if (! profile) { g_message ("%s", error->message); g_clear_error (&error); } else if (! gimp_lcms_profile_is_rgb (profile)) { g_message (_("Color profile '%s' is not for RGB color space."), gimp_filename_to_utf8 (config->rgb_profile)); cmsCloseProfile (profile); profile = NULL; } else { rgb_filename = config->rgb_profile; } g_object_unref (file); } if (! profile) profile = gimp_lcms_create_srgb_profile (); name = gimp_lcms_profile_get_label (profile); label = g_strdup_printf (_("RGB workspace (%s)"), name); g_free (name); cmsCloseProfile (profile); gimp_color_profile_combo_box_add (GIMP_COLOR_PROFILE_COMBO_BOX (combo), rgb_filename, label); g_free (label); gimp_color_profile_combo_box_set_active (GIMP_COLOR_PROFILE_COMBO_BOX (combo), filename, NULL); return combo; }
bool LcmsColorProfileContainer::init() { if (d->profile) { cmsCloseProfile(d->profile); } d->profile = cmsOpenProfileFromMem((void *)d->data->rawData().constData(), d->data->rawData().size()); #ifndef NDEBUG if (d->data->rawData().size() == 4096) { qWarning() << "Profile has a size of 4096, which is suspicious and indicates a possible misuse of QIODevice::read(int), check your code."; } #endif if (d->profile) { wchar_t buffer[_BUFFER_SIZE_]; d->colorSpaceSignature = cmsGetColorSpace(d->profile); d->deviceClass = cmsGetDeviceClass(d->profile); cmsGetProfileInfo(d->profile, cmsInfoDescription, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->name = QString::fromWCharArray(buffer); //apparantly this should give us a localised string??? Not sure about this. cmsGetProfileInfo(d->profile, cmsInfoModel, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->productDescription = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoManufacturer, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->manufacturer = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoCopyright, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->copyright = QString::fromWCharArray(buffer); cmsProfileClassSignature profile_class; profile_class = cmsGetDeviceClass(d->profile); d->valid = (profile_class != cmsSigNamedColorClass); //This is where obtain the whitepoint, and convert it to the actual white point of the profile in the case a Chromatic adaption tag is //present. This is necessary for profiles following the v4 spec. cmsCIEXYZ baseMediaWhitePoint;//dummy to hold copy of mediawhitepoint if this is modified by chromatic adaption. if (cmsIsTag(d->profile, cmsSigMediaWhitePointTag)) { d->mediaWhitePoint = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigMediaWhitePointTag)); baseMediaWhitePoint = d->mediaWhitePoint; cmsXYZ2xyY(&d->whitePoint, &d->mediaWhitePoint); if (cmsIsTag(d->profile, cmsSigChromaticAdaptationTag)) { //the chromatic adaption tag represent a matrix from the actual white point of the profile to D50. cmsCIEXYZ *CAM1 = (cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigChromaticAdaptationTag); //We first put all our data into structures we can manipulate. double d3dummy [3] = {d->mediaWhitePoint.X, d->mediaWhitePoint.Y, d->mediaWhitePoint.Z}; QGenericMatrix<1, 3, double> whitePointMatrix(d3dummy); QTransform invertDummy(CAM1[0].X, CAM1[0].Y, CAM1[0].Z, CAM1[1].X, CAM1[1].Y, CAM1[1].Z, CAM1[2].X, CAM1[2].Y, CAM1[2].Z); //we then abuse QTransform's invert function because it probably does matrix invertion 20 times better than I can program. //if the matrix is uninvertable, invertedDummy will be an identity matrix, which for us means that it won't give any noticeble //effect when we start multiplying. QTransform invertedDummy = invertDummy.inverted(); //we then put the QTransform into a generic 3x3 matrix. double d9dummy [9] = {invertedDummy.m11(), invertedDummy.m12(), invertedDummy.m13(), invertedDummy.m21(), invertedDummy.m22(), invertedDummy.m23(), invertedDummy.m31(), invertedDummy.m32(), invertedDummy.m33() }; QGenericMatrix<3, 3, double> chromaticAdaptionMatrix(d9dummy); //multiplying our inverted adaption matrix with the whitepoint gives us the right whitepoint. QGenericMatrix<1, 3, double> result = chromaticAdaptionMatrix * whitePointMatrix; //and then we pour the matrix into the whitepoint variable. Generic matrix does row/column for indices even though it //uses column/row for initialising. d->mediaWhitePoint.X = result(0, 0); d->mediaWhitePoint.Y = result(1, 0); d->mediaWhitePoint.Z = result(2, 0); cmsXYZ2xyY(&d->whitePoint, &d->mediaWhitePoint); } } //This is for RGB profiles, but it only works for matrix profiles. Need to design it to work with non-matrix profiles. if (cmsIsTag(d->profile, cmsSigRedColorantTag)) { cmsCIEXYZTRIPLE tempColorants; tempColorants.Red = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigRedColorantTag)); tempColorants.Green = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigGreenColorantTag)); tempColorants.Blue = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigBlueColorantTag)); //convert to d65, this is useless. cmsAdaptToIlluminant(&d->colorants.Red, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Red); cmsAdaptToIlluminant(&d->colorants.Green, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Green); cmsAdaptToIlluminant(&d->colorants.Blue, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Blue); //d->colorants = tempColorants; d->hasColorants = true; } else { //qDebug()<<d->name<<": has no colorants"; d->hasColorants = false; } //retrieve TRC. if (cmsIsTag(d->profile, cmsSigRedTRCTag) && cmsIsTag(d->profile, cmsSigBlueTRCTag) && cmsIsTag(d->profile, cmsSigGreenTRCTag)) { d->redTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigRedTRCTag)); d->greenTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigGreenTRCTag)); d->blueTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigBlueTRCTag)); d->redTRCReverse = cmsReverseToneCurve(d->redTRC); d->greenTRCReverse = cmsReverseToneCurve(d->greenTRC); d->blueTRCReverse = cmsReverseToneCurve(d->blueTRC); d->hasTRC = true; } else if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { d->grayTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigGrayTRCTag)); d->grayTRCReverse = cmsReverseToneCurve(d->grayTRC); d->hasTRC = true; } else { d->hasTRC = false; } // Check if the profile can convert (something->this) d->suitableForOutput = cmsIsMatrixShaper(d->profile) || (cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT) && cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)); d->version = cmsGetProfileVersion(d->profile); d->defaultIntent = cmsGetHeaderRenderingIntent(d->profile); d->isMatrixShaper = cmsIsMatrixShaper(d->profile); d->isPerceptualCLUT = cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT); d->isSaturationCLUT = cmsIsCLUT(d->profile, INTENT_SATURATION, LCMS_USED_AS_INPUT); d->isAbsoluteCLUT = cmsIsCLUT(d->profile, INTENT_SATURATION, LCMS_USED_AS_INPUT); d->isRelativeCLUT = cmsIsCLUT(d->profile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_INPUT); return true; } return false; }
LcmsColorProfileContainer::~LcmsColorProfileContainer() { cmsCloseProfile(d->profile); delete d; }
static int TransformImage(TIFF* in, TIFF* out, const char *cDefInpProf, const char *cOutProf) { cmsHPROFILE hIn, hOut, hProof, hInkLimit = NULL; cmsHTRANSFORM xform; DWORD wInput, wOutput; int OutputColorSpace; int bps = (Width16 ? 2 : 1); DWORD dwFlags = 0; int nPlanes; // Observer adaptation state (only meaningful on absolute colorimetric intent) cmsSetAdaptationState(ObserverAdaptationState); if (EmbedProfile && cOutProf) DoEmbedProfile(out, cOutProf); if (BlackWhiteCompensation) dwFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION; if (PreserveBlack) { dwFlags |= cmsFLAGS_PRESERVEBLACK; if (PrecalcMode == 0) PrecalcMode = 1; } switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOTPRECALC; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; case 1: break; default: FatalError("Unknown precalculation mode '%d'", PrecalcMode); } if (GamutCheck) dwFlags |= cmsFLAGS_GAMUTCHECK; hProof = NULL; hOut = NULL; if (lIsDeviceLink) { hIn = cmsOpenProfileFromFile(cDefInpProf, "r"); } else { hIn = GetTIFFProfile(in); if (hIn == NULL) hIn = OpenStockProfile(cDefInpProf); hOut = OpenStockProfile(cOutProf); if (cProofing != NULL) { hProof = OpenStockProfile(cProofing); dwFlags |= cmsFLAGS_SOFTPROOFING; } } // Take input color space wInput = GetInputPixelType(in); // Assure both, input profile and input TIFF are on same colorspace if (_cmsLCMScolorSpace(cmsGetColorSpace(hIn)) != (int) T_COLORSPACE(wInput)) FatalError("Input profile is not operating in proper color space"); if (!lIsDeviceLink) OutputColorSpace = _cmsLCMScolorSpace(cmsGetColorSpace(hOut)); else OutputColorSpace = _cmsLCMScolorSpace(cmsGetPCS(hIn)); wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, bps); WriteOutputTags(out, OutputColorSpace, bps); CopyOtherTags(in, out); // Ink limit if (InkLimit != 400.0 && (OutputColorSpace == PT_CMYK || OutputColorSpace == PT_CMY)) { cmsHPROFILE hProfiles[10]; int nProfiles = 0; hInkLimit = cmsCreateInkLimitingDeviceLink(cmsGetColorSpace(hOut), InkLimit); hProfiles[nProfiles++] = hIn; if (hProof) { hProfiles[nProfiles++] = hProof; hProfiles[nProfiles++] = hProof; } hProfiles[nProfiles++] = hOut; hProfiles[nProfiles++] = hInkLimit; xform = cmsCreateMultiprofileTransform(hProfiles, nProfiles, wInput, wOutput, Intent, dwFlags); } else { xform = cmsCreateProofingTransform(hIn, wInput, hOut, wOutput, hProof, Intent, ProofingIntent, dwFlags); } // Planar stuff if (T_PLANAR(wInput)) nPlanes = T_CHANNELS(wInput) + T_EXTRA(wInput); else nPlanes = 1; // TIFF Lab of 8 bits need special handling if (wInput == TYPE_Lab_8 && !InputLabUsingICC && cInpProf != NULL && stricmp(cInpProf, "*Lab") == 0) { cmsSetUserFormatters(xform, TYPE_Lab_8, UnrollTIFFLab8, TYPE_Lab_8, NULL); } if (wOutput == TYPE_Lab_8 && cOutProf != NULL && stricmp(cOutProf, "*Lab") == 0) { cmsSetUserFormatters(xform, TYPE_Lab_8, NULL, TYPE_Lab_8, PackTIFFLab8); } // Handle tile by tile or strip by strip if (TIFFIsTiled(in)) { TileBasedXform(xform, in, out, nPlanes); } else { StripBasedXform(xform, in, out, nPlanes); } cmsDeleteTransform(xform); cmsCloseProfile(hIn); cmsCloseProfile(hOut); if (hInkLimit) cmsCloseProfile(hInkLimit); if (hProof) cmsCloseProfile(hProof); TIFFWriteDirectory(out); return 1; }
static GimpPDBStatusType lcms_icc_apply (GimpColorConfig *config, GimpRunMode run_mode, gint32 image, const gchar *filename, GimpColorRenderingIntent intent, gboolean bpc, gboolean *dont_ask) { GimpPDBStatusType status = GIMP_PDB_SUCCESS; cmsHPROFILE src_profile = NULL; cmsHPROFILE dest_profile = NULL; guchar src_md5[16]; guchar dest_md5[16]; g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), GIMP_PDB_CALLING_ERROR); g_return_val_if_fail (image != -1, GIMP_PDB_CALLING_ERROR); if (! filename) filename = config->rgb_profile; if (filename) { dest_profile = lcms_load_profile (filename, dest_md5); if (! dest_profile) return GIMP_PDB_EXECUTION_ERROR; if (! lcms_icc_profile_is_rgb (dest_profile)) { g_message (_("Color profile '%s' is not for RGB color space."), gimp_filename_to_utf8 (filename)); cmsCloseProfile (dest_profile); return GIMP_PDB_EXECUTION_ERROR; } } src_profile = lcms_image_get_profile (config, image, src_md5); if (src_profile && ! lcms_icc_profile_is_rgb (src_profile)) { g_printerr ("lcms: attached color profile is not for RGB color space " "(skipping)\n"); cmsCloseProfile (src_profile); src_profile = NULL; } if (! src_profile && ! dest_profile) return GIMP_PDB_SUCCESS; if (! src_profile) { src_profile = cmsCreate_sRGBProfile (); lcms_sRGB_checksum (src_md5); } if (! dest_profile) { dest_profile = cmsCreate_sRGBProfile (); lcms_sRGB_checksum (dest_md5); } if (memcmp (src_md5, dest_md5, 16) == 0) { gchar *src_desc = lcms_icc_profile_get_desc (src_profile); gchar *dest_desc = lcms_icc_profile_get_desc (dest_profile); cmsCloseProfile (src_profile); cmsCloseProfile (dest_profile); g_printerr ("lcms: skipping conversion because profiles seem to be equal:\n"); g_printerr (" %s\n", src_desc); g_printerr (" %s\n", dest_desc); g_free (src_desc); g_free (dest_desc); return GIMP_PDB_SUCCESS; } if (run_mode == GIMP_RUN_INTERACTIVE && ! lcms_icc_apply_dialog (image, src_profile, dest_profile, dont_ask)) { status = GIMP_PDB_CANCEL; } if (status == GIMP_PDB_SUCCESS && ! lcms_image_apply_profile (image, src_profile, dest_profile, filename, intent, bpc)) { status = GIMP_PDB_EXECUTION_ERROR; } cmsCloseProfile (src_profile); cmsCloseProfile (dest_profile); return status; }
static void cdisplay_lcms_changed (GimpColorDisplay *display) { CdisplayLcms *lcms = CDISPLAY_LCMS (display); GimpColorConfig *config = gimp_color_display_get_config (display); cmsHPROFILE src_profile = NULL; cmsHPROFILE dest_profile = NULL; cmsHPROFILE proof_profile = NULL; cmsUInt16Number alarmCodes[cmsMAXCHANNELS] = { 0, }; if (lcms->transform) { cmsDeleteTransform (lcms->transform); lcms->transform = NULL; } if (! config) return; switch (config->mode) { case GIMP_COLOR_MANAGEMENT_OFF: return; case GIMP_COLOR_MANAGEMENT_SOFTPROOF: proof_profile = cdisplay_lcms_get_printer_profile (lcms); /* fallthru */ case GIMP_COLOR_MANAGEMENT_DISPLAY: src_profile = cdisplay_lcms_get_rgb_profile (lcms); dest_profile = cdisplay_lcms_get_display_profile (lcms); break; } if (proof_profile) { cmsUInt32Number softproof_flags = 0; if (! src_profile) src_profile = gimp_lcms_create_srgb_profile (); if (! dest_profile) dest_profile = gimp_lcms_create_srgb_profile (); softproof_flags |= cmsFLAGS_SOFTPROOFING; if (config->simulation_use_black_point_compensation) { softproof_flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } if (config->simulation_gamut_check) { guchar r, g, b; softproof_flags |= cmsFLAGS_GAMUTCHECK; gimp_rgb_get_uchar (&config->out_of_gamut_color, &r, &g, &b); alarmCodes[0] = (cmsUInt16Number) r * 256; alarmCodes[1] = (cmsUInt16Number) g * 256; alarmCodes[2] = (cmsUInt16Number) b * 256; cmsSetAlarmCodes (alarmCodes); } lcms->transform = cmsCreateProofingTransform (src_profile, TYPE_RGBA_FLT, dest_profile, TYPE_RGBA_FLT, proof_profile, config->simulation_intent, config->display_intent, softproof_flags); cmsCloseProfile (proof_profile); } else if (src_profile || dest_profile) { cmsUInt32Number display_flags = 0; if (! src_profile) src_profile = gimp_lcms_create_srgb_profile (); if (! dest_profile) dest_profile = gimp_lcms_create_srgb_profile (); if (config->display_use_black_point_compensation) { display_flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } lcms->transform = cmsCreateTransform (src_profile, TYPE_RGBA_FLT, dest_profile, TYPE_RGBA_FLT, config->display_intent, display_flags); } if (dest_profile) cmsCloseProfile (dest_profile); if (src_profile) cmsCloseProfile (src_profile); }
static gboolean lcms_image_set_profile (gint32 image, cmsHPROFILE profile, const gchar *filename, gboolean undo_group) { g_return_val_if_fail (image != -1, FALSE); if (filename) { GimpParasite *parasite; GMappedFile *file; GError *error = NULL; file = g_mapped_file_new (filename, FALSE, &error); if (! file) { g_message ("%s", error->message); g_error_free (error); return FALSE; } /* check that this file is actually an ICC profile */ if (! profile) { profile = cmsOpenProfileFromMem (g_mapped_file_get_contents (file), g_mapped_file_get_length (file)); if (profile) { cmsCloseProfile (profile); } else { g_message (_("'%s' does not appear to be an ICC color profile"), gimp_filename_to_utf8 (filename)); return FALSE; } } if (undo_group) gimp_image_undo_group_start (image); parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, g_mapped_file_get_length (file), g_mapped_file_get_contents (file)); g_mapped_file_unref (file); gimp_image_attach_parasite (image, parasite); gimp_parasite_free (parasite); } else { if (undo_group) gimp_image_undo_group_start (image); gimp_image_detach_parasite (image, "icc-profile"); } gimp_image_detach_parasite (image, "icc-profile-name"); if (undo_group) gimp_image_undo_group_end (image); return TRUE; }
static int TransformImage(char *cDefInpProf, char *cOutProf) { cmsHPROFILE hIn, hOut, hProof; cmsHTRANSFORM xform; cmsUInt32Number wInput, wOutput; int OutputColorSpace; cmsUInt32Number dwFlags = 0; cmsUInt32Number EmbedLen; cmsUInt8Number* EmbedBuffer; cmsSetAdaptationState(ObserverAdaptationState); if (BlackPointCompensation) { dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } switch (PrecalcMode) { case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break; case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break; case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break; default:; } if (GamutCheck) { dwFlags |= cmsFLAGS_GAMUTCHECK; cmsSetAlarmCodes(Alarm); } // Take input color space wInput = GetInputPixelType(); if (lIsDeviceLink) { hIn = cmsOpenProfileFromFile(cDefInpProf, "r"); hOut = NULL; hProof = NULL; } else { if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen)) { hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen); if (Verbose) { fprintf(stdout, " (Embedded profile found)\n"); PrintProfileInformation(hIn); fflush(stdout); } if (hIn != NULL && SaveEmbedded != NULL) SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded); free(EmbedBuffer); } else { // Default for ITU/Fax if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab) cDefInpProf = "*Lab"; if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0) hIn = CreateITU2PCS_ICC(); else hIn = OpenStockProfile(0, cDefInpProf); } if (cOutProf != NULL && cmsstrcasecmp(cOutProf, "*lab") == 0) hOut = CreatePCS2ITU_ICC(); else hOut = OpenStockProfile(0, cOutProf); hProof = NULL; if (cProofing != NULL) { hProof = OpenStockProfile(0, cProofing); if (hProof == NULL) { FatalError("Proofing profile couldn't be read."); } dwFlags |= cmsFLAGS_SOFTPROOFING; } } if (!hIn) FatalError("Input profile couldn't be read."); if (!hOut) FatalError("Output profile couldn't be read."); // Assure both, input profile and input JPEG are on same colorspace if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput))) FatalError("Input profile is not operating in proper color space"); // Output colorspace is given by output profile if (lIsDeviceLink) { OutputColorSpace = GetDevicelinkColorSpace(hIn); } else { OutputColorSpace = GetProfileColorSpace(hOut); } jpeg_copy_critical_parameters(&Decompressor, &Compressor); WriteOutputFields(OutputColorSpace); wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace); xform = cmsCreateProofingTransform(hIn, wInput, hOut, wOutput, hProof, Intent, ProofingIntent, dwFlags); if (xform == NULL) FatalError("Cannot transform by using the profiles"); DoTransform(xform, OutputColorSpace); jcopy_markers_execute(&Decompressor, &Compressor); cmsDeleteTransform(xform); cmsCloseProfile(hIn); cmsCloseProfile(hOut); if (hProof) cmsCloseProfile(hProof); return 1; }
// This function creates a profile based on White point, primaries and // transfer functions. cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries, cmsToneCurve* const TransferFunction[3]) { cmsHPROFILE hICC; cmsMAT3 MColorants; cmsCIEXYZTRIPLE Colorants; cmsCIExyY MaxWhite; cmsMAT3 CHAD; cmsCIEXYZ WhitePointXYZ; hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) // can't allocate return NULL; cmsSetProfileVersion(hICC, 4.3); cmsSetDeviceClass(hICC, cmsSigDisplayClass); cmsSetColorSpace(hICC, cmsSigRgbData); cmsSetPCS(hICC, cmsSigXYZData); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Implement profile using following tags: // // 1 cmsSigProfileDescriptionTag // 2 cmsSigMediaWhitePointTag // 3 cmsSigRedColorantTag // 4 cmsSigGreenColorantTag // 5 cmsSigBlueColorantTag // 6 cmsSigRedTRCTag // 7 cmsSigGreenTRCTag // 8 cmsSigBlueTRCTag // 9 Chromatic adaptation Tag // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II) // 10 cmsSigChromaticityTag if (!SetTextTags(hICC, L"RGB built-in")) goto Error; if (WhitePoint) { if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; cmsxyY2XYZ(&WhitePointXYZ, WhitePoint); _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ()); // This is a V4 tag, but many CMM does read and understand it no matter which version if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error; } if (WhitePoint && Primaries) { MaxWhite.x = WhitePoint -> x; MaxWhite.y = WhitePoint -> y; MaxWhite.Y = 1.0; if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error; Colorants.Red.X = MColorants.v[0].n[0]; Colorants.Red.Y = MColorants.v[1].n[0]; Colorants.Red.Z = MColorants.v[2].n[0]; Colorants.Green.X = MColorants.v[0].n[1]; Colorants.Green.Y = MColorants.v[1].n[1]; Colorants.Green.Z = MColorants.v[2].n[1]; Colorants.Blue.X = MColorants.v[0].n[2]; Colorants.Blue.Y = MColorants.v[1].n[2]; Colorants.Blue.Z = MColorants.v[2].n[2]; if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error; if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error; if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error; } if (TransferFunction) { // Tries to minimize space. Thanks to Richard Hughes for this nice idea if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) 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 (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error; } return hICC; Error: if (hICC) cmsCloseProfile(hICC); return NULL; }
static int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; cmsUInt32Number nChannels; cmsUInt32Number InputFormat; int rc; cmsHPROFILE Profiles[2]; cmsCIEXYZ BlackPointAdaptedToD50; // Does create a device-link based transform. // The DeviceLink is next dumped as working CSA. InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); nChannels = T_CHANNELS(InputFormat); cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); // Adjust output to Lab4 hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); Profiles[0] = hProfile; Profiles[1] = hLab; xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0); cmsCloseProfile(hLab); if (xform == NULL) { cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); return 0; } // Only 1, 3 and 4 channels are allowed switch (nChannels) { case 1: { cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); cmsFreeToneCurve(Gray2Y); } break; case 3: case 4: { cmsUInt32Number OutFrm = TYPE_Lab_16; cmsPipeline* DeviceLink; _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; DeviceLink = cmsPipelineDup(v ->Lut); if (DeviceLink == NULL) return 0; dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); cmsPipelineFree(DeviceLink); if (rc == 0) return 0; } break; default: cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels); return 0; } cmsDeleteTransform(xform); return 1; }
// Does convert a transform into a device link profile cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) { cmsHPROFILE hProfile = NULL; cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut; _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; cmsPipeline* LUT = NULL; cmsStage* mpe; cmsContext ContextID = cmsGetTransformContextID(hTransform); const cmsAllowedLUT* AllowedLUT; cmsTagSignature DestinationTag; cmsProfileClassSignature deviceClass; _cmsAssert(hTransform != NULL); // Get the first mpe to check for named color mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); // Check if is a named color transform if (mpe != NULL) { if (cmsStageType(mpe) == cmsSigNamedColorElemType) { return CreateNamedColorDevicelink(hTransform); } } // First thing to do is to get a copy of the transformation LUT = cmsPipelineDup(xform ->Lut); if (LUT == NULL) return NULL; // Time to fix the Lab2/Lab4 issue. if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) goto Error; } // On the output side too if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) goto Error; } hProfile = cmsCreateProfilePlaceholder(ContextID); if (!hProfile) goto Error; // can't allocate cmsSetProfileVersion(hProfile, Version); FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); // Optimize the LUT and precalculate a devicelink ChansIn = cmsChannelsOf(xform -> EntryColorSpace); ChansOut = cmsChannelsOf(xform -> ExitColorSpace); ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); deviceClass = cmsGetDeviceClass(hProfile); if (deviceClass == cmsSigOutputClass) DestinationTag = cmsSigBToA0Tag; else DestinationTag = cmsSigAToB0Tag; // Check if the profile/version can store the result if (dwFlags & cmsFLAGS_FORCE_CLUT) AllowedLUT = NULL; else AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); if (AllowedLUT == NULL) { // Try to optimize _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); } // If no way, then force CLUT that for sure can be written if (AllowedLUT == NULL) { dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); // Put identity curves if needed if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType) if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) goto Error; if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType) if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) goto Error; AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); } // Somethings is wrong... if (AllowedLUT == NULL) { goto Error; } if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); // Tag profile with information if (!SetTextTags(hProfile, L"devicelink")) goto Error; // Store result if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; if (xform -> InputColorant != NULL) { if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; } if (xform -> OutputColorant != NULL) { if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; } if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; } // Set the white point if (deviceClass == cmsSigInputClass) { if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; } else { if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; } // Per 7.2.15 in spec 4.3 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); cmsPipelineFree(LUT); return hProfile; Error: if (LUT != NULL) cmsPipelineFree(LUT); cmsCloseProfile(hProfile); return NULL; }
static void cms_profile_dealloc(CmsProfileObject* self) { (void) cmsCloseProfile(self->profile); PyObject_Del(self); }
cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit) { cmsHPROFILE hICC; cmsPipeline* LUT; cmsStage* CLUT; int nChannels; if (ColorSpace != cmsSigCmykData) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); return NULL; } if (Limit < 0.0 || Limit > 400) { cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400"); if (Limit < 0) Limit = 0; if (Limit > 400) Limit = 400; } hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) // can't allocate return NULL; cmsSetProfileVersion(hICC, 4.3); cmsSetDeviceClass(hICC, cmsSigLinkClass); cmsSetColorSpace(hICC, ColorSpace); cmsSetPCS(hICC, ColorSpace); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Creates a Pipeline with 3D grid only LUT = cmsPipelineAlloc(ContextID, 4, 4); if (LUT == NULL) goto Error; nChannels = cmsChannelsOf(ColorSpace); CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL); if (CLUT == NULL) goto Error; if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error; if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) || !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) || !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels))) goto Error; // Create tags if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error; if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error; if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error; // cmsPipeline is already on virtual profile cmsPipelineFree(LUT); // Ok, done return hICC; Error: if (LUT != NULL) cmsPipelineFree(LUT); if (hICC != NULL) cmsCloseProfile(hICC); return NULL; }
// Get the display ICC profile of the monitor associated with the widget. // For X display, uses the ICC profile specifications version 0.2 from // http://burtonini.com/blog/computers/xicc // Based on code from Gimp's modules/cdisplay_lcms.c void dt_ctl_set_display_profile() { if(!dt_control_running()) return; // make sure that no one gets a broken profile // FIXME: benchmark if the try is really needed when moving/resizing the window. Maybe we can just lock it and block if(pthread_rwlock_trywrlock(&darktable.control->xprofile_lock)) return; // we are already updating the profile. Or someone is reading right now. Too bad we can't distinguish that. Whatever ... GtkWidget *widget = dt_ui_center(darktable.gui->ui); guint8 *buffer = NULL; gint buffer_size = 0; gchar *profile_source = NULL; #if defined GDK_WINDOWING_X11 // we will use the xatom no matter what configured when compiled without colord gboolean use_xatom = TRUE; #if defined USE_COLORDGTK gboolean use_colord = TRUE; gchar *display_profile_source = dt_conf_get_string("ui_last/display_profile_source"); if(display_profile_source) { if(!strcmp(display_profile_source, "xatom")) use_colord = FALSE; else if(!strcmp(display_profile_source, "colord")) use_xatom = FALSE; g_free(display_profile_source); } #endif /* let's have a look at the xatom, just in case ... */ if(use_xatom) { GdkScreen *screen = gtk_widget_get_screen(widget); if ( screen==NULL ) screen = gdk_screen_get_default(); int monitor = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window(widget)); char *atom_name; if (monitor > 0) atom_name = g_strdup_printf("_ICC_PROFILE_%d", monitor); else atom_name = g_strdup("_ICC_PROFILE"); profile_source = g_strdup_printf("xatom %s", atom_name); GdkAtom type = GDK_NONE; gint format = 0; gdk_property_get(gdk_screen_get_root_window(screen), gdk_atom_intern(atom_name, FALSE), GDK_NONE, 0, 64 * 1024 * 1024, FALSE, &type, &format, &buffer_size, &buffer); g_free(atom_name); } #ifdef USE_COLORDGTK /* also try to get the profile from colord. this will set the value asynchronously! */ if(use_colord) { CdWindow *window = cd_window_new(); GtkWidget *center_widget = dt_ui_center(darktable.gui->ui); cd_window_get_profile(window, center_widget, NULL, dt_ctl_get_display_profile_colord_callback, NULL); } #endif #elif defined GDK_WINDOWING_QUARTZ GdkScreen *screen = gtk_widget_get_screen(widget); if ( screen==NULL ) screen = gdk_screen_get_default(); int monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(widget)); CGDirectDisplayID ids[monitor + 1]; uint32_t total_ids; CMProfileRef prof = NULL; if(CGGetOnlineDisplayList(monitor + 1, &ids[0], &total_ids) == kCGErrorSuccess && total_ids == monitor + 1) CMGetProfileByAVID(ids[monitor], &prof); if ( prof!=NULL ) { CFDataRef data; data = CMProfileCopyICCData(NULL, prof); CMCloseProfile(prof); UInt8 *tmp_buffer = (UInt8 *) g_malloc(CFDataGetLength(data)); CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), tmp_buffer); buffer = (guint8 *) tmp_buffer; buffer_size = CFDataGetLength(data); CFRelease(data); } profile_source = g_strdup("osx color profile api"); #elif defined G_OS_WIN32 (void)widget; HDC hdc = GetDC (NULL); if ( hdc!=NULL ) { DWORD len = 0; GetICMProfile (hdc, &len, NULL); gchar *path = g_new (gchar, len); if (GetICMProfile (hdc, &len, path)) { gsize size; g_file_get_contents(path, (gchar**)&buffer, &size, NULL); buffer_size = size; } g_free (path); ReleaseDC (NULL, hdc); } profile_source = g_strdup("windows color profile api"); #endif int profile_changed = buffer_size > 0 && (darktable.control->xprofile_size != buffer_size || memcmp(darktable.control->xprofile_data, buffer, buffer_size) != 0); if(profile_changed) { cmsHPROFILE profile = NULL; char name[512]; // thanks to ufraw for this! g_free(darktable.control->xprofile_data); darktable.control->xprofile_data = buffer; darktable.control->xprofile_size = buffer_size; profile = cmsOpenProfileFromMem(buffer, buffer_size); if(profile) { dt_colorspaces_get_profile_name(profile, "en", "US", name, sizeof(name)); cmsCloseProfile(profile); } dt_print(DT_DEBUG_CONTROL, "[color profile] we got a new screen profile `%s' from the %s (size: %d)\n", *name?name:"(unknown)", profile_source, buffer_size); } pthread_rwlock_unlock(&darktable.control->xprofile_lock); if(profile_changed) dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_PROFILE_CHANGED); g_free(profile_source); }
cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, int nLUTPoints, cmsFloat64Number Bright, cmsFloat64Number Contrast, cmsFloat64Number Hue, cmsFloat64Number Saturation, int TempSrc, int TempDest) { cmsHPROFILE hICC; cmsPipeline* Pipeline; BCHSWADJUSTS bchsw; cmsCIExyY WhitePnt; cmsStage* CLUT; cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; int i; 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; cmsSetDeviceClass(hICC, cmsSigAbstractClass); cmsSetColorSpace(hICC, cmsSigLabData); cmsSetPCS(hICC, cmsSigLabData); cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Creates a Pipeline with 3D grid only Pipeline = cmsPipelineAlloc(ContextID, 3, 3); if (Pipeline == NULL) { cmsCloseProfile(hICC); return NULL; } for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints; CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL); if (CLUT == NULL) return NULL; if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) { // Shouldn't reach here goto Error; } if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) { goto Error; } // Create tags if (!SetTextTags(hICC, L"BCHS built-in")) return NULL; cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ()); cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline); // Pipeline is already on virtual profile cmsPipelineFree(Pipeline); // Ok, done return hICC; Error: cmsPipelineFree(Pipeline); cmsCloseProfile(hICC); return NULL; }
void CLASS apply_profile (const char *input, const char *output) { char *prof; cmsHPROFILE hInProfile=0, hOutProfile=0; cmsHTRANSFORM hTransform; FILE *fp; unsigned size; #ifndef USE_LCMS2 cmsErrorAction (LCMS_ERROR_SHOW); #endif if (strcmp (input, "embed")) hInProfile = cmsOpenProfileFromFile (input, "r"); else if (profile_length) { #ifndef LIBRAW_LIBRARY_BUILD prof = (char *) malloc (profile_length); merror (prof, "apply_profile()"); fseek (ifp, profile_offset, SEEK_SET); fread (prof, 1, profile_length, ifp); hInProfile = cmsOpenProfileFromMem (prof, profile_length); free (prof); #else hInProfile = cmsOpenProfileFromMem (imgdata.color.profile, profile_length); #endif } else { #ifdef LIBRAW_LIBRARY_BUILD imgdata.process_warnings |= LIBRAW_WARN_NO_EMBEDDED_PROFILE; #endif #ifdef DCRAW_VERBOSE fprintf (stderr,_("%s has no embedded profile.\n"), ifname); #endif } if (!hInProfile) { #ifdef LIBRAW_LIBRARY_BUILD imgdata.process_warnings |= LIBRAW_WARN_NO_INPUT_PROFILE; #endif return; } if (!output) hOutProfile = cmsCreate_sRGBProfile(); else if ((fp = fopen (output, "rb"))) { fread (&size, 4, 1, fp); fseek (fp, 0, SEEK_SET); oprof = (unsigned *) malloc (size = ntohl(size)); merror (oprof, "apply_profile()"); fread (oprof, 1, size, fp); fclose (fp); if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) { free (oprof); oprof = 0; } } #ifdef DCRAW_VERBOSE else fprintf (stderr,_("Cannot open file %s!\n"), output); #endif if (!hOutProfile) { #ifdef LIBRAW_LIBRARY_BUILD imgdata.process_warnings |= LIBRAW_WARN_BAD_OUTPUT_PROFILE; #endif goto quit; } #ifdef DCRAW_VERBOSE if (verbose) fprintf (stderr,_("Applying color profile...\n")); #endif #ifdef LIBRAW_LIBRARY_BUILD RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE,0,2); #endif hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); cmsDoTransform (hTransform, image, image, width*height); raw_color = 1; /* Don't use rgb_cam with a profile */ cmsDeleteTransform (hTransform); cmsCloseProfile (hOutProfile); quit: cmsCloseProfile (hInProfile); #ifdef LIBRAW_LIBRARY_BUILD RUN_CALLBACK(LIBRAW_PROGRESS_APPLY_PROFILE,1,2); #endif }
void dt_colorspaces_cleanup_profile(cmsHPROFILE p) { cmsCloseProfile(p); }
cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, cmsHPROFILE hProfiles[], cmsBool BPC[], cmsUInt32Number Intents[], cmsFloat64Number AdaptationStates[], cmsUInt32Number nGamutPCSposition, cmsHPROFILE hGamut) { cmsHPROFILE hLab; cmsPipeline* Gamut; cmsStage* CLUT; cmsUInt32Number dwFormat; GAMUTCHAIN Chain; int nChannels, nGridpoints; cmsColorSpaceSignature ColorSpace; cmsUInt32Number i; cmsHPROFILE ProfileList[256]; cmsBool BPCList[256]; cmsFloat64Number AdaptationList[256]; cmsUInt32Number IntentList[256]; memset(&Chain, 0, sizeof(GAMUTCHAIN)); if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); return NULL; } hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); if (hLab == NULL) return NULL; // The figure of merit. On matrix-shaper profiles, should be almost zero as // the conversion is pretty exact. On LUT based profiles, different resolutions // of input and output CLUT may result in differences. if (cmsIsMatrixShaper(hGamut)) { Chain.Thereshold = 1.0; } else { Chain.Thereshold = ERR_THERESHOLD; } // Create a copy of parameters for (i=0; i < nGamutPCSposition; i++) { ProfileList[i] = hProfiles[i]; BPCList[i] = BPC[i]; AdaptationList[i] = AdaptationStates[i]; IntentList[i] = Intents[i]; } // Fill Lab identity ProfileList[nGamutPCSposition] = hLab; BPCList[nGamutPCSposition] = 0; AdaptationList[nGamutPCSposition] = 1.0; IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; ColorSpace = cmsGetColorSpace(hGamut); nChannels = cmsChannelsOf(ColorSpace); nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); // 16 bits to Lab double Chain.hInput = cmsCreateExtendedTransform(ContextID, nGamutPCSposition + 1, ProfileList, BPCList, IntentList, AdaptationList, NULL, 0, dwFormat, TYPE_Lab_DBL, cmsFLAGS_NOCACHE); // Does create the forward step. Lab double to device dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); Chain.hForward = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_DBL, hGamut, dwFormat, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE); // Does create the backwards step Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE); // All ok? if (Chain.hInput && Chain.hForward && Chain.hReverse) { // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing // dE when doing a transform back and forth on the colorimetric intent. Gamut = cmsPipelineAlloc(ContextID, 3, 1); 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... // 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; }
/** * mcm_picker_refresh_results: **/ static void mcm_picker_refresh_results (void) { McmXyz *xyz = NULL; GtkImage *image; GtkLabel *label; GdkPixbuf *pixbuf = NULL; gdouble color_xyz[3]; guint8 color_rgb[3]; gdouble color_lab[3]; gdouble color_error[3]; gchar *text_xyz = NULL; gchar *text_lab = NULL; gchar *text_rgb = NULL; gchar *text_error = NULL; cmsHPROFILE profile_xyz; cmsHPROFILE profile_rgb; cmsHPROFILE profile_lab; cmsHTRANSFORM transform_rgb; cmsHTRANSFORM transform_lab; cmsHTRANSFORM transform_error; /* nothing set yet */ if (profile_filename == NULL) goto out; /* get new value */ g_object_get (calibrate, "xyz", &xyz, NULL); /* create new pixbuf of the right size */ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 200, 200); /* get values */ g_object_get (xyz, "cie-x", &color_xyz[0], "cie-y", &color_xyz[1], "cie-z", &color_xyz[2], NULL); /* lcms scales these for some reason */ color_xyz[0] /= 100.0f; color_xyz[1] /= 100.0f; color_xyz[2] /= 100.0f; /* get profiles */ profile_xyz = cmsCreateXYZProfile (); profile_rgb = cmsOpenProfileFromFile (profile_filename, "r"); profile_lab = cmsCreateLab4Profile (cmsD50_xyY ()); /* create transforms */ transform_rgb = cmsCreateTransform (profile_xyz, TYPE_XYZ_DBL, profile_rgb, TYPE_RGB_8, INTENT_PERCEPTUAL, 0); if (transform_rgb == NULL) goto out; transform_lab = cmsCreateTransform (profile_xyz, TYPE_XYZ_DBL, profile_lab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0); if (transform_lab == NULL) goto out; transform_error = cmsCreateTransform (profile_rgb, TYPE_RGB_8, profile_xyz, TYPE_XYZ_DBL, INTENT_PERCEPTUAL, 0); if (transform_error == NULL) goto out; cmsDoTransform (transform_rgb, color_xyz, color_rgb, 1); cmsDoTransform (transform_lab, color_xyz, color_lab, 1); cmsDoTransform (transform_error, color_rgb, color_error, 1); /* destroy lcms state */ cmsDeleteTransform (transform_rgb); cmsDeleteTransform (transform_lab); cmsDeleteTransform (transform_error); cmsCloseProfile (profile_xyz); cmsCloseProfile (profile_rgb); cmsCloseProfile (profile_lab); /* set XYZ */ label = GTK_LABEL (gtk_builder_get_object (builder, "label_xyz")); text_xyz = g_strdup_printf ("%.3f, %.3f, %.3f", color_xyz[0], color_xyz[1], color_xyz[2]); gtk_label_set_label (label, text_xyz); /* set LAB */ label = GTK_LABEL (gtk_builder_get_object (builder, "label_lab")); text_lab = g_strdup_printf ("%.3f, %.3f, %.3f", color_lab[0], color_lab[1], color_lab[2]); gtk_label_set_label (label, text_lab); /* set RGB */ label = GTK_LABEL (gtk_builder_get_object (builder, "label_rgb")); text_rgb = g_strdup_printf ("%i, %i, %i (#%02X%02X%02X)", color_rgb[0], color_rgb[1], color_rgb[2], color_rgb[0], color_rgb[1], color_rgb[2]); gtk_label_set_label (label, text_rgb); mcm_picker_set_pixbuf_color (pixbuf, color_rgb[0], color_rgb[1], color_rgb[2]); /* set error */ label = GTK_LABEL (gtk_builder_get_object (builder, "label_error")); text_error = g_strdup_printf ("%.1f%%, %.1f%%, %.1f%%", ABS ((color_error[0] - color_xyz[0]) / color_xyz[0] * 100), ABS ((color_error[1] - color_xyz[1]) / color_xyz[1] * 100), ABS ((color_error[2] - color_xyz[2]) / color_xyz[2] * 100)); gtk_label_set_label (label, text_error); /* set image */ image = GTK_IMAGE (gtk_builder_get_object (builder, "image_preview")); gtk_image_set_from_pixbuf (image, pixbuf); out: g_free (text_xyz); g_free (text_lab); g_free (text_rgb); g_free (text_error); if (xyz != NULL) g_object_unref (xyz); if (pixbuf != NULL) g_object_unref (pixbuf); }