diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index ee836a032e8..6acfd32bf7b 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 41 +#define BLENDER_FILE_SUBVERSION 42 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/BKE_image_format.hh b/source/blender/blenkernel/BKE_image_format.hh index 347132cdfe2..5f2976eb834 100644 --- a/source/blender/blenkernel/BKE_image_format.hh +++ b/source/blender/blenkernel/BKE_image_format.hh @@ -35,6 +35,11 @@ void BKE_image_format_update_color_space_for_type(ImageFormatData *format); void BKE_image_format_blend_read_data(BlendDataReader *reader, ImageFormatData *imf); void BKE_image_format_blend_write(BlendWriter *writer, ImageFormatData *imf); +/* Sets the media type of the given format that belongs to the given ID. This involves updating the + * imtype to a default format if it does not match the newly set media type. */ +void BKE_image_format_media_type_set(ImageFormatData *format, + ID *owner_id, + const MediaType media_type); void BKE_image_format_set(ImageFormatData *imf, ID *owner_id, const char imtype); /* File Paths */ @@ -97,6 +102,11 @@ int BKE_image_path_ext_from_imtype_ensure(char *filepath, size_t filepath_maxncp char BKE_ftype_to_imtype(int ftype, const ImbFormatOptions *options); int BKE_imtype_to_ftype(char imtype, ImbFormatOptions *r_options); +/* Returns true if the given imtype represents an image. This excludes multi-layer images, use + * BKE_imtype_is_multi_layer_image to detect those images. */ +bool BKE_imtype_is_image(char imtype); +/* Returns true if the given imtype represents a multi-layer image. */ +bool BKE_imtype_is_multi_layer_image(char imtype); bool BKE_imtype_is_movie(char imtype); bool BKE_imtype_supports_compress(char imtype); bool BKE_imtype_supports_quality(char imtype); diff --git a/source/blender/blenkernel/intern/image_format.cc b/source/blender/blenkernel/intern/image_format.cc index e640d7c8076..7ac3bf95d96 100644 --- a/source/blender/blenkernel/intern/image_format.cc +++ b/source/blender/blenkernel/intern/image_format.cc @@ -90,10 +90,53 @@ void BKE_image_format_blend_write(BlendWriter *writer, ImageFormatData *imf) BKE_color_managed_view_settings_blend_write(writer, &imf->view_settings); } +void BKE_image_format_media_type_set(ImageFormatData *format, + ID *owner_id, + const MediaType media_type) +{ + format->media_type = media_type; + + switch (media_type) { + case MEDIA_TYPE_IMAGE: + if (!BKE_imtype_is_image(format->imtype)) { + BKE_image_format_set(format, owner_id, R_IMF_IMTYPE_PNG); + } + break; + case MEDIA_TYPE_MULTI_LAYER_IMAGE: + if (!BKE_imtype_is_multi_layer_image(format->imtype)) { + BKE_image_format_set(format, owner_id, R_IMF_IMTYPE_MULTILAYER); + } + break; + case MEDIA_TYPE_VIDEO: + if (!BKE_imtype_is_movie(format->imtype)) { + BKE_image_format_set(format, owner_id, R_IMF_IMTYPE_FFMPEG); + } + break; + } +} + void BKE_image_format_set(ImageFormatData *imf, ID *owner_id, const char imtype) { imf->imtype = imtype; + /* Update media type in case it doesn't match the new imtype. Note that normally, one would use + * the BKE_image_format_media_type_set function to set the media type, but that function itself + * calls this function to update the imtype, and while this wouldn't case recursion since the + * imtype is already conforming, it is better to err on the side of caution and set the media + * type manually. */ + if (BKE_imtype_is_image(imf->imtype)) { + imf->media_type = MEDIA_TYPE_IMAGE; + } + else if (BKE_imtype_is_multi_layer_image(imf->imtype)) { + imf->media_type = MEDIA_TYPE_MULTI_LAYER_IMAGE; + } + else if (BKE_imtype_is_movie(imf->imtype)) { + imf->media_type = MEDIA_TYPE_VIDEO; + } + else { + BLI_assert_unreachable(); + } + const bool is_render = (owner_id && GS(owner_id->name) == ID_SCE); /* see note below on why this is */ const char chan_flag = BKE_imtype_valid_channels(imf->imtype) | @@ -253,6 +296,20 @@ char BKE_ftype_to_imtype(const int ftype, const ImbFormatOptions *options) return R_IMF_IMTYPE_JPEG90; } +bool BKE_imtype_is_image(const char imtype) +{ + return !BKE_imtype_is_multi_layer_image(imtype) && !BKE_imtype_is_movie(imtype); +} + +bool BKE_imtype_is_multi_layer_image(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_MULTILAYER: + return true; + } + return false; +} + bool BKE_imtype_is_movie(const char imtype) { switch (imtype) { diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index 86802de8836..6a93d38aa32 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -35,6 +35,7 @@ #include "BKE_colortools.hh" #include "BKE_curves.hh" #include "BKE_idprop.hh" +#include "BKE_image_format.hh" #include "BKE_lib_id.hh" #include "BKE_main.hh" #include "BKE_mesh_legacy_convert.hh" @@ -1217,6 +1218,23 @@ static void do_version_composite_node_in_scene_tree(bNodeTree &node_tree, bNode version_node_remove(node_tree, node); } +/* Updates the media type of the given format to match its imtype. */ +static void update_format_media_type(ImageFormatData *format) +{ + if (BKE_imtype_is_image(format->imtype)) { + format->media_type = MEDIA_TYPE_IMAGE; + } + else if (BKE_imtype_is_multi_layer_image(format->imtype)) { + format->media_type = MEDIA_TYPE_MULTI_LAYER_IMAGE; + } + else if (BKE_imtype_is_movie(format->imtype)) { + format->media_type = MEDIA_TYPE_VIDEO; + } + else { + BLI_assert_unreachable(); + } +} + void do_versions_after_linking_500(FileData *fd, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 9)) { @@ -1643,6 +1661,36 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain) } } + /* ImageFormatData gained a new media type which we need to be set according to the existing + * imtype. */ + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 42)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + update_format_media_type(&scene->r.im_format); + } + + FOREACH_NODETREE_BEGIN (bmain, node_tree, id) { + if (node_tree->type != NTREE_COMPOSIT) { + continue; + } + + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + if (node->type_legacy != CMP_NODE_OUTPUT_FILE) { + continue; + } + + NodeImageMultiFile *storage = static_cast(node->storage); + update_format_media_type(&storage->format); + + LISTBASE_FOREACH (bNodeSocket *, input, &node->inputs) { + NodeImageMultiFileSocket *input_storage = static_cast( + input->storage); + update_format_media_type(&input_storage->format); + } + } + } + FOREACH_NODETREE_END; + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/editors/space_image/image_buttons.cc b/source/blender/editors/space_image/image_buttons.cc index 53981f925c1..ce58262479b 100644 --- a/source/blender/editors/space_image/image_buttons.cc +++ b/source/blender/editors/space_image/image_buttons.cc @@ -993,7 +993,13 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma col->use_property_split_set(true); col->use_property_decorate_set(false); - col->prop(imfptr, "file_format", UI_ITEM_NONE, std::nullopt, ICON_NONE); + col->prop(imfptr, "media_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); + + /* Multi layer images and video media types only have a single supported format, so we needn't + * draw the format format enum. */ + if (imf->media_type == MEDIA_TYPE_IMAGE) { + col->prop(imfptr, "file_format", UI_ITEM_NONE, std::nullopt, ICON_NONE); + } /* Multi-layer always saves raw unmodified channels. */ if (imf->imtype != R_IMF_IMTYPE_MULTILAYER) { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 47a6be02614..6f223b7f1e3 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -462,6 +462,8 @@ typedef enum eStereo3dInterlaceType { * RNA ensures these enum's are only selectable for render output. */ typedef struct ImageFormatData { + /** MediaType. */ + char media_type; /** * R_IMF_IMTYPE_PNG, R_... * \note Video types should only ever be set from this structure when used from #RenderData. @@ -487,11 +489,6 @@ typedef struct ImageFormatData { /** OpenEXR: R_IMF_EXR_CODEC_* values in low OPENEXR_CODEC_MASK bits. */ char exr_codec; - /** CINEON. */ - char cineon_flag; - short cineon_white, cineon_black; - float cineon_gamma; - /** Jpeg2000. */ char jp2_flag; char jp2_codec; @@ -499,7 +496,12 @@ typedef struct ImageFormatData { /** TIFF. */ char tiff_codec; - char _pad[4]; + /** CINEON. */ + char cineon_flag; + short cineon_white, cineon_black; + float cineon_gamma; + + char _pad[3]; /** Multi-view. */ char views_format; @@ -514,6 +516,13 @@ typedef struct ImageFormatData { ColorManagedColorspaceSettings linear_colorspace_settings; } ImageFormatData; +/** #ImageFormatData::media_type */ +typedef enum MediaType { + MEDIA_TYPE_IMAGE = 0, + MEDIA_TYPE_MULTI_LAYER_IMAGE = 1, + MEDIA_TYPE_VIDEO = 2, +} MediaType; + /** #ImageFormatData::imtype */ enum { R_IMF_IMTYPE_TARGA = 0, diff --git a/source/blender/makesrna/RNA_enum_items.hh b/source/blender/makesrna/RNA_enum_items.hh index 3211c16f06f..e471fc9404a 100644 --- a/source/blender/makesrna/RNA_enum_items.hh +++ b/source/blender/makesrna/RNA_enum_items.hh @@ -56,7 +56,7 @@ DEF_ENUM(rna_enum_shrinkwrap_type_items) DEF_ENUM(rna_enum_shrinkwrap_face_cull_items) DEF_ENUM(rna_enum_node_warning_type_items) -DEF_ENUM(rna_enum_image_type_items) +DEF_ENUM(rna_enum_image_type_all_items) DEF_ENUM(rna_enum_image_color_mode_items) DEF_ENUM(rna_enum_image_color_depth_items) DEF_ENUM(rna_enum_image_generated_type_items) diff --git a/source/blender/makesrna/intern/rna_image.cc b/source/blender/makesrna/intern/rna_image.cc index f62edfc6ab1..9650d24f4e4 100644 --- a/source/blender/makesrna/intern/rna_image.cc +++ b/source/blender/makesrna/intern/rna_image.cc @@ -1163,7 +1163,7 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "File Name", "Image/Movie file name (without data refreshing)"); prop = RNA_def_property(srna, "file_format", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_image_type_items); + RNA_def_property_enum_items(prop, rna_enum_image_type_all_items); RNA_def_property_enum_funcs( prop, "rna_Image_file_format_get", "rna_Image_file_format_set", nullptr); RNA_def_property_ui_text(prop, "File Format", "Format used for re-saving this file"); diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index cdad373950e..b47912f5ddb 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -255,6 +255,34 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { {0, nullptr, 0, nullptr, nullptr}, }; +#define MEDIA_TYPE_ENUM_IMAGE \ + { \ + MEDIA_TYPE_IMAGE, "IMAGE", ICON_NONE, "Image", "" \ + } +#define MEDIA_TYPE_ENUM_MULTI_LAYER_IMAGE \ + { \ + MEDIA_TYPE_MULTI_LAYER_IMAGE, "MULTI_LAYER_IMAGE", ICON_NONE, "Multi-Layer EXR", "" \ + } +#define MEDIA_TYPE_ENUM_VIDEO \ + { \ + MEDIA_TYPE_VIDEO, "VIDEO", ICON_NONE, "Video", "" \ + } + +static const EnumPropertyItem rna_enum_media_type_all_items[] = { + MEDIA_TYPE_ENUM_IMAGE, + MEDIA_TYPE_ENUM_MULTI_LAYER_IMAGE, + MEDIA_TYPE_ENUM_VIDEO, + {0, nullptr, 0, nullptr, nullptr}, +}; + +#ifdef RNA_RUNTIME +static const EnumPropertyItem rna_enum_media_type_image_items[] = { + MEDIA_TYPE_ENUM_IMAGE, + MEDIA_TYPE_ENUM_MULTI_LAYER_IMAGE, + {0, nullptr, 0, nullptr, nullptr}, +}; +#endif + /* workaround for duplicate enums, * have each enum line as a define then conditionally set it or not */ @@ -337,7 +365,13 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { # define R_IMF_ENUM_WEBP #endif -#define IMAGE_TYPE_ITEMS_IMAGE_ONLY \ +#ifdef WITH_FFMPEG +# define R_IMF_ENUM_FFMPEG {R_IMF_IMTYPE_FFMPEG, "FFMPEG", ICON_FILE_MOVIE, "FFmpeg Video", ""}, +#else +# define R_IMF_ENUM_FFMPEG +#endif + +#define IMAGE_TYPE_ITEMS_IMAGE \ R_IMF_ENUM_BMP \ /* DDS save not supported yet R_IMF_ENUM_DDS */ \ R_IMF_ENUM_IRIS \ @@ -346,27 +380,36 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { R_IMF_ENUM_JPEG2K \ R_IMF_ENUM_TAGA \ R_IMF_ENUM_TAGA_RAW \ - RNA_ENUM_ITEM_SEPR_COLUMN, R_IMF_ENUM_CINEON R_IMF_ENUM_DPX R_IMF_ENUM_EXR_MULTILAYER \ - R_IMF_ENUM_EXR R_IMF_ENUM_HDR R_IMF_ENUM_TIFF R_IMF_ENUM_WEBP + RNA_ENUM_ITEM_SEPR_COLUMN, R_IMF_ENUM_CINEON R_IMF_ENUM_DPX R_IMF_ENUM_EXR R_IMF_ENUM_HDR \ + R_IMF_ENUM_TIFF R_IMF_ENUM_WEBP + +#define IMAGE_TYPE_ITEMS_MULTI_LAYER_IMAGE R_IMF_ENUM_EXR_MULTILAYER + +#define IMAGE_TYPE_ITEMS_VIDEO R_IMF_ENUM_FFMPEG #ifdef RNA_RUNTIME -static const EnumPropertyItem image_only_type_items[] = { +static const EnumPropertyItem image_type_items[] = { + IMAGE_TYPE_ITEMS_IMAGE - IMAGE_TYPE_ITEMS_IMAGE_ONLY + {0, nullptr, 0, nullptr, nullptr}, +}; + +static const EnumPropertyItem multi_layer_image_type_items[] = { + IMAGE_TYPE_ITEMS_MULTI_LAYER_IMAGE + + {0, nullptr, 0, nullptr, nullptr}, +}; + +static const EnumPropertyItem video_image_type_items[] = { + IMAGE_TYPE_ITEMS_VIDEO {0, nullptr, 0, nullptr, nullptr}, }; #endif -const EnumPropertyItem rna_enum_image_type_items[] = { - RNA_ENUM_ITEM_HEADING(N_("Image"), nullptr), +const EnumPropertyItem rna_enum_image_type_all_items[] = { + IMAGE_TYPE_ITEMS_IMAGE IMAGE_TYPE_ITEMS_MULTI_LAYER_IMAGE IMAGE_TYPE_ITEMS_VIDEO - IMAGE_TYPE_ITEMS_IMAGE_ONLY - - RNA_ENUM_ITEM_HEADING(N_("Movie"), nullptr), -#ifdef WITH_FFMPEG - {R_IMF_IMTYPE_FFMPEG, "FFMPEG", ICON_FILE_MOVIE, "FFmpeg Video", ""}, -#endif {0, nullptr, 0, nullptr, nullptr}, }; @@ -1389,6 +1432,30 @@ static bool rna_RenderSettings_is_movie_format_get(PointerRNA *ptr) return BKE_imtype_is_movie(rd->im_format.imtype); } +static const EnumPropertyItem *rna_ImageFormatSettings_media_type_itemf(bContext * /*C*/, + PointerRNA *ptr, + PropertyRNA * /*prop*/, + bool * /*r_free*/) +{ + ID *id = ptr->owner_id; + /* Scene format setting include video, so we return all items. Otherwise, only image types are + * returned. */ + if (id && GS(id->name) == ID_SCE) { + return rna_enum_media_type_all_items; + } + else { + return rna_enum_media_type_image_items; + } +} + +/* If the existing imtype does not match the new media type, assign an appropriate default media + * type. */ +static void rna_ImageFormatSettings_media_type_set(PointerRNA *ptr, int value) +{ + ImageFormatData *format = ptr->data_as(); + BKE_image_format_media_type_set(format, ptr->owner_id, static_cast(value)); +} + static void rna_ImageFormatSettings_file_format_set(PointerRNA *ptr, int value) { BKE_image_format_set((ImageFormatData *)ptr->data, ptr->owner_id, value); @@ -1399,13 +1466,17 @@ static const EnumPropertyItem *rna_ImageFormatSettings_file_format_itemf(bContex PropertyRNA * /*prop*/, bool * /*r_free*/) { - ID *id = ptr->owner_id; - if (id && GS(id->name) == ID_SCE) { - return rna_enum_image_type_items; - } - else { - return image_only_type_items; + const ImageFormatData *format = ptr->data_as(); + switch (static_cast(format->media_type)) { + case MEDIA_TYPE_IMAGE: + return image_type_items; + case MEDIA_TYPE_MULTI_LAYER_IMAGE: + return multi_layer_image_type_items; + case MEDIA_TYPE_VIDEO: + return video_image_type_items; } + + return rna_enum_image_type_all_items; } static const EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext * /*C*/, @@ -6188,7 +6259,6 @@ static void rna_def_image_format_stereo3d_format(BlenderRNA *brna) static void rna_def_scene_image_format_data(BlenderRNA *brna) { - # ifdef WITH_IMAGE_OPENJPEG static const EnumPropertyItem jp2_codec_items[] = { {R_IMF_JP2_CODEC_JP2, "JP2", 0, "JP2", ""}, @@ -6222,9 +6292,19 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_ImageFormatSettings_path"); RNA_def_struct_ui_text(srna, "Image Format", "Settings for image formats"); + prop = RNA_def_property(srna, "media_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "media_type"); + RNA_def_property_enum_items(prop, rna_enum_media_type_all_items); + RNA_def_property_enum_funcs(prop, + nullptr, + "rna_ImageFormatSettings_media_type_set", + "rna_ImageFormatSettings_media_type_itemf"); + RNA_def_property_ui_text(prop, "Media Type", "The type of media to save"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + prop = RNA_def_property(srna, "file_format", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, nullptr, "imtype"); - RNA_def_property_enum_items(prop, rna_enum_image_type_items); + RNA_def_property_enum_items(prop, rna_enum_image_type_all_items); RNA_def_property_enum_funcs(prop, nullptr, "rna_ImageFormatSettings_file_format_set", @@ -7165,7 +7245,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) # if 0 /* moved */ prop = RNA_def_property(srna, "file_format", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, nullptr, "imtype"); - RNA_def_property_enum_items(prop, rna_enum_image_type_items); + RNA_def_property_enum_items(prop, rna_enum_image_type_all_items); RNA_def_property_enum_funcs(prop, nullptr, "rna_RenderSettings_file_format_set", nullptr); RNA_def_property_ui_text(prop, "File Format", "File format to save the rendered images as"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);