diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index 54dc03c0edb..1039bc56e60 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -140,6 +140,14 @@ enum { MAINIDRELATIONS_INCLUDE_UI = 1 << 0, }; +struct MainColorspace { + /* + * A colorspace, view or display was not found, which likely means the OpenColorIO config + * used to create this blend file is missing. + */ + bool is_missing_opencolorio_config = false; +}; + struct Main : blender::NonCopyable, blender::NonMovable { /** * Runtime vector storing all split Mains (one Main for each library data), during readfile or @@ -251,6 +259,11 @@ struct Main : blender::NonCopyable, blender::NonMovable { */ Library *curlib = nullptr; + /* + * Colorspace information for this file. + */ + MainColorspace colorspace; + /* List bases for all ID types, containing all IDs for the current #Main. */ ListBase scenes = {}; diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index 61d661789aa..c46d80b0de4 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -472,12 +472,14 @@ bool BKE_main_is_empty(Main *bmain) bool BKE_main_has_issues(const Main *bmain) { - return bmain->has_forward_compatibility_issues || bmain->is_asset_edit_file; + return bmain->has_forward_compatibility_issues || bmain->is_asset_edit_file || + bmain->colorspace.is_missing_opencolorio_config; } bool BKE_main_needs_overwrite_confirm(const Main *bmain) { - return bmain->has_forward_compatibility_issues || bmain->is_asset_edit_file; + return bmain->has_forward_compatibility_issues || bmain->is_asset_edit_file || + bmain->colorspace.is_missing_opencolorio_config; } void BKE_main_lock(Main *bmain) diff --git a/source/blender/editors/interface/templates/interface_template_status.cc b/source/blender/editors/interface/templates/interface_template_status.cc index 5d611be352f..0e32f2135a8 100644 --- a/source/blender/editors/interface/templates/interface_template_status.cc +++ b/source/blender/editors/interface/templates/interface_template_status.cc @@ -389,6 +389,13 @@ static std::string ui_template_status_tooltip(bContext *C, tooltip_message += RPT_( "This file is managed by the Blender asset system and cannot be overridden"); } + if (bmain->colorspace.is_missing_opencolorio_config) { + if (!tooltip_message.empty()) { + tooltip_message += "\n\n"; + } + tooltip_message += RPT_( + "Displays, views or color spaces in this file were missing and have been changed"); + } return tooltip_message; } @@ -503,7 +510,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) blender::StringRefNull version_string = ED_info_statusbar_string_ex( bmain, scene, view_layer, STATUSBAR_SHOW_VERSION); - blender::StringRefNull warning_message; + std::string warning_message; /* Blender version part is shown as warning area when there are forward compatibility issues with * currently loaded .blend file. */ @@ -517,6 +524,14 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) } } + /* Color space warning. */ + if (bmain->colorspace.is_missing_opencolorio_config) { + if (!warning_message.empty()) { + warning_message = warning_message + " "; + } + warning_message = warning_message + RPT_("Color Management"); + } + const uiStyle *style = UI_style_get(); uiLayout *ui_abs = &layout->absolute(false); uiBlock *block = ui_abs->block(); @@ -545,7 +560,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) /*# ButType::Roundbox's background color is set in `but->col`. */ UI_GetThemeColor4ubv(TH_WARNING, but->col); - if (!warning_message.is_empty()) { + if (!warning_message.empty()) { /* Background for the rest of the message. */ but = uiDefBut(block, ButType::Roundbox, @@ -586,7 +601,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */ /* The warning message, if any. */ - if (!warning_message.is_empty()) { + if (!warning_message.empty()) { but = uiDefBut(block, ButType::But, 0, diff --git a/source/blender/imbuf/intern/colormanagement.cc b/source/blender/imbuf/intern/colormanagement.cc index c8ff3a05880..82f3b768bd2 100644 --- a/source/blender/imbuf/intern/colormanagement.cc +++ b/source/blender/imbuf/intern/colormanagement.cc @@ -894,7 +894,7 @@ void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace) /** \name Generic Functions * \{ */ -static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, +static bool colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings, const char *what, const ocio::Display *default_display) { @@ -902,12 +902,12 @@ static void colormanage_check_display_settings(ColorManagedDisplaySettings *disp if (display_name.is_empty()) { STRNCPY_UTF8(display_settings->display_device, default_display->name().c_str()); - return; + return true; } const ocio::Display *display = g_config->get_display_by_name(display_name); if (display) { - return; + return true; } StringRefNull new_display_name = default_display->name(); @@ -929,6 +929,7 @@ static void colormanage_check_display_settings(ColorManagedDisplaySettings *disp new_display_name.c_str()); STRNCPY_UTF8(display_settings->display_device, new_display_name.c_str()); + return false; } static StringRefNull colormanage_find_matching_view_name(const ocio::Display *display, @@ -958,14 +959,17 @@ static StringRefNull colormanage_find_matching_view_name(const ocio::Display *di return ""; } -static void colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings, +static bool colormanage_check_view_settings(ColorManagedDisplaySettings *display_settings, ColorManagedViewSettings *view_settings, const char *what) { const ocio::Display *display = g_config->get_display_by_name(display_settings->display_device); if (!display) { - return; + return false; } + + bool ok = true; + const char *default_look_name = IMB_colormanagement_look_get_default_name(); StringRefNull view_name = view_settings->view_transform; @@ -986,6 +990,7 @@ static void colormanage_check_view_settings(ColorManagedDisplaySettings *display view_settings->view_transform, new_view_name.c_str()); STRNCPY_UTF8(view_settings->view_transform, new_view_name.c_str()); + ok = false; } } } @@ -1003,6 +1008,7 @@ static void colormanage_check_view_settings(ColorManagedDisplaySettings *display default_look_name); STRNCPY_UTF8(view_settings->look, default_look_name); + ok = false; } else if (!colormanage_compatible_look(look, view_settings->view_transform)) { CLOG_INFO(&LOG, @@ -1015,6 +1021,7 @@ static void colormanage_check_view_settings(ColorManagedDisplaySettings *display default_look_name); STRNCPY_UTF8(view_settings->look, default_look_name); + ok = false; } } @@ -1023,10 +1030,13 @@ static void colormanage_check_view_settings(ColorManagedDisplaySettings *display view_settings->exposure = 0.0f; view_settings->gamma = 1.0f; } + + return ok; } -static void colormanage_check_colorspace_name(char *name, const char *what) +static bool colormanage_check_colorspace_name(char *name, const char *what) { + bool ok = true; if (name[0] == '\0') { /* pass */ } @@ -1036,24 +1046,18 @@ static void colormanage_check_colorspace_name(char *name, const char *what) if (!colorspace) { CLOG_WARN(&LOG, "%s colorspace \"%s\" not found, will use default instead.", what, name); name[0] = '\0'; + ok = false; } } (void)what; + return ok; } -static void colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *settings, +static bool colormanage_check_colorspace_settings(ColorManagedColorspaceSettings *settings, const char *what) { - colormanage_check_colorspace_name(settings->name, what); -} - -static bool strip_callback(Strip *strip, void * /*user_data*/) -{ - if (strip->data) { - colormanage_check_colorspace_name(strip->data->colorspace_settings.name, "sequencer strip"); - } - return true; + return colormanage_check_colorspace_name(settings->name, what); } void IMB_colormanagement_check_file_config(Main *bmain) @@ -1064,17 +1068,22 @@ void IMB_colormanagement_check_file_config(Main *bmain) return; } + /* Check display, view and colorspace names in datablocks. */ + bool is_missing_opencolorio_config = false; + /* Check scenes. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ColorManagedColorspaceSettings *sequencer_colorspace_settings; + bool ok = true; /* check scene color management settings */ - colormanage_check_display_settings(&scene->display_settings, "scene", default_display); - colormanage_check_view_settings(&scene->display_settings, &scene->view_settings, "scene"); + ok &= colormanage_check_display_settings(&scene->display_settings, "scene", default_display); + ok &= colormanage_check_view_settings( + &scene->display_settings, &scene->view_settings, "scene"); sequencer_colorspace_settings = &scene->sequencer_colorspace_settings; - colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer"); + ok &= colormanage_check_colorspace_settings(sequencer_colorspace_settings, "sequencer"); if (sequencer_colorspace_settings->name[0] == '\0') { STRNCPY_UTF8(sequencer_colorspace_settings->name, global_role_default_sequencer); @@ -1082,36 +1091,57 @@ void IMB_colormanagement_check_file_config(Main *bmain) /* Check sequencer strip input colorspace. */ if (scene->ed != nullptr) { - blender::seq::for_each_callback(&scene->ed->seqbase, strip_callback, nullptr); + blender::seq::for_each_callback(&scene->ed->seqbase, [&](Strip *strip) { + if (strip->data) { + ok &= colormanage_check_colorspace_settings(&strip->data->colorspace_settings, + "sequencer strip"); + } + return true; + }); } + + is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&scene->id)); } /* Check image and movie input colorspace. */ LISTBASE_FOREACH (Image *, image, &bmain->images) { - colormanage_check_colorspace_settings(&image->colorspace_settings, "image"); + const bool ok = colormanage_check_colorspace_settings(&image->colorspace_settings, "image"); + is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&image->id)); } LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) { - colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip"); + const bool ok = colormanage_check_colorspace_settings(&clip->colorspace_settings, "clip"); + is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&clip->id)); } /* Check compositing nodes. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type == NTREE_COMPOSIT) { + bool ok = true; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type_legacy == CMP_NODE_CONVERT_TO_DISPLAY) { NodeConvertToDisplay *nctd = static_cast(node->storage); - colormanage_check_display_settings(&nctd->display_settings, "node", default_display); - colormanage_check_view_settings(&nctd->display_settings, &nctd->view_settings, "node"); + ok &= colormanage_check_display_settings( + &nctd->display_settings, "node", default_display); + ok &= colormanage_check_view_settings( + &nctd->display_settings, &nctd->view_settings, "node"); } else if (node->type_legacy == CMP_NODE_CONVERT_COLOR_SPACE) { NodeConvertColorSpace *ncs = static_cast(node->storage); - colormanage_check_colorspace_name(ncs->from_color_space, "node"); - colormanage_check_colorspace_name(ncs->to_color_space, "node"); + ok &= colormanage_check_colorspace_name(ncs->from_color_space, "node"); + ok &= colormanage_check_colorspace_name(ncs->to_color_space, "node"); } } + is_missing_opencolorio_config |= (!ok && !ID_IS_LINKED(&ntree->id)); } } + + /* Inform users about mismatch, but not for new files. Linked datablocks are also ignored, + * as we are not overwriting them on blend file save which is the main purpose of this + * warning. */ + bmain->colorspace.is_missing_opencolorio_config = (bmain->filepath[0] == '\0') ? + false : + is_missing_opencolorio_config; } void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings, diff --git a/source/blender/makesrna/intern/rna_main.cc b/source/blender/makesrna/intern/rna_main.cc index 7e85df38c15..dac5df28d79 100644 --- a/source/blender/makesrna/intern/rna_main.cc +++ b/source/blender/makesrna/intern/rna_main.cc @@ -82,6 +82,18 @@ static void rna_Main_filepath_set(PointerRNA *ptr, const char *value) } # endif +static PointerRNA rna_Main_colorspace_get(PointerRNA *ptr) +{ + Main *bmain = (Main *)ptr->data; + return PointerRNA(nullptr, &RNA_BlendFileColorspace, &bmain->colorspace); +} + +static bool rna_MainColorspace_is_missing_opencolorio_config_get(PointerRNA *ptr) +{ + MainColorspace *colorspace = ptr->data_as(); + return colorspace->is_missing_opencolorio_config; +} + # define RNA_MAIN_LISTBASE_FUNCS_DEF(_listbase_name) \ static void rna_Main_##_listbase_name##_begin(CollectionPropertyIterator *iter, \ PointerRNA *ptr) \ @@ -165,6 +177,26 @@ struct MainCollectionDef { CollectionDefFunc *func; }; +static void rna_def_main_colorspace(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "BlendFileColorspace", nullptr); + RNA_def_struct_ui_text(srna, + "Blend-File Color Space", + "Information about the color space used for datablocks in a blend file"); + + prop = RNA_def_property(srna, "is_missing_opencolorio_config", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs( + prop, "rna_MainColorspace_is_missing_opencolorio_config_get", nullptr); + RNA_def_property_ui_text(prop, + "Missing OpenColorIO Configuration", + "A color space, view or display was not found, which likely means the " + "OpenColorIO config used to create this blend file is missing"); +} + void RNA_def_main(BlenderRNA *brna) { StructRNA *srna; @@ -470,6 +502,17 @@ void RNA_def_main(BlenderRNA *brna) } } + rna_def_main_colorspace(brna); + + prop = RNA_def_property(srna, "colorspace", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "BlendFileColorspace"); + RNA_def_property_pointer_funcs(prop, "rna_Main_colorspace_get", nullptr, nullptr, nullptr); + RNA_def_property_ui_text( + prop, + "Color Space", + "Information about the color space used for datablocks in a blend file"); + RNA_api_main(srna); # ifdef UNIT_TEST diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 917b007f023..aac1519c679 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -3791,6 +3791,7 @@ static wmOperatorStatus wm_save_as_mainfile_exec(bContext *C, wmOperator *op) /* If saved file is the active one, there are technically no more compatibility issues, the * file on disk now matches the currently opened data version-wise. */ bmain->has_forward_compatibility_issues = false; + bmain->colorspace.is_missing_opencolorio_config = false; /* If saved file is the active one, notify WM so that saved status and window title can be * updated. */ @@ -4382,6 +4383,14 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma ICON_NONE); layout->label(RPT_("saved as a new, regular file."), ICON_NONE); } + + if (bmain->colorspace.is_missing_opencolorio_config) { + layout->label( + RPT_("Displays, views or color spaces in this file were missing and have been changed."), + ICON_NONE); + layout->label(RPT_("Saving it with this OpenColorIO configuration may cause loss of data."), + ICON_NONE); + } } static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/) @@ -4487,10 +4496,18 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re true, false); } - else { + else if (!bmain->colorspace.is_missing_opencolorio_config) { BLI_assert_unreachable(); } + if (bmain->colorspace.is_missing_opencolorio_config) { + uiItemL_ex(layout, + RPT_("Overwrite file with current OpenColorIO configuration?"), + ICON_NONE, + true, + false); + } + /* Filename. */ const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C)); char filename[FILE_MAX]; @@ -4604,7 +4621,8 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0'; if (file_has_been_saved_before) { - if (bmain->has_forward_compatibility_issues) { + if (bmain->has_forward_compatibility_issues || bmain->colorspace.is_missing_opencolorio_config) + { /* Need to invoke to get the file-browser and choose where to save the new file. * This also makes it impossible to keep on going with current operation, which is why * callback cannot be executed anymore.