Fix #140375: Image editor save overwrites movie file

Create a good default name for saving individual frames of a movie file loaded
as an image datablock, instead of the movie file name.

Changes ImBuf to store the frame separate from the filepath, to implement this.
Seems more clear for ImBuf.filepath to be an actual filepath anyway.

Thanks to Jesse and Aras for investigating this bug.

Pull Request: https://projects.blender.org/blender/blender/pulls/140471
This commit is contained in:
Brecht Van Lommel
2025-06-17 14:15:19 +02:00
parent 9eb16ef8c5
commit a0fe1e3ac5
5 changed files with 23 additions and 6 deletions
@@ -146,6 +146,15 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
* by the image saving code itself. */
BKE_image_user_file_path_ex(bmain, iuser, ima, opts->filepath, false, false);
/* For movies, replace extension and add the frame number to avoid writing over the movie file
* itself and provide a good default file path. */
if (ima->source == IMA_SRC_MOVIE) {
BLI_path_extension_strip(opts->filepath);
SNPRINTF(opts->filepath, "%s_%.*d", opts->filepath, 4, ibuf->fileframe);
BKE_image_path_ext_from_imformat_ensure(
opts->filepath, sizeof(opts->filepath), &opts->im_format);
}
/* sanitize all settings */
/* unlikely but just in case */
@@ -465,6 +465,7 @@ struct UndoImageBuf {
UndoImageBuf *post;
char ibuf_filepath[IMB_FILEPATH_SIZE];
int ibuf_fileframe;
UndoImageTile **tiles;
@@ -496,6 +497,7 @@ static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf)
MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__));
STRNCPY(ubuf->ibuf_filepath, ibuf->filepath);
ubuf->ibuf_fileframe = ibuf->fileframe;
ubuf->image_state.source = image->source;
ubuf->image_state.use_float = ibuf->float_buffer.data != nullptr;
@@ -665,10 +667,11 @@ static void uhandle_free_list(ListBase *undo_handles)
static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh,
const Image * /*image*/,
const char *ibuf_filepath)
const char *ibuf_filepath,
const int ibuf_fileframe)
{
LISTBASE_FOREACH (UndoImageBuf *, ubuf, &uh->buffers) {
if (STREQ(ubuf->ibuf_filepath, ibuf_filepath)) {
if (STREQ(ubuf->ibuf_filepath, ibuf_filepath) && ubuf->ibuf_fileframe == ibuf_fileframe) {
return ubuf;
}
}
@@ -677,7 +680,7 @@ static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh,
static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
{
BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->filepath) == nullptr);
BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->filepath, ibuf->fileframe) == nullptr);
UndoImageBuf *ubuf = ubuf_from_image_no_tiles(image, ibuf);
BLI_addtail(&uh->buffers, ubuf);
@@ -688,7 +691,7 @@ static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *
static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
{
UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->filepath);
UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->filepath, ibuf->fileframe);
if (ubuf == nullptr) {
ubuf = uhandle_add_ubuf(uh, image, ibuf);
}
@@ -771,7 +774,8 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
/* Use name lookup because the pointer is cleared for previous steps. */
UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image, tile_number);
if (uh_prev != nullptr) {
UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_filepath);
UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(
uh_prev, image, ubuf->ibuf_filepath, ubuf->ibuf_fileframe);
if (ubuf_reference) {
ubuf_reference = ubuf_reference->post;
if ((ubuf_reference->image_dims[0] == ubuf->image_dims[0]) &&
+2
View File
@@ -249,6 +249,8 @@ struct ImBuf {
ImbFormatOptions foptions;
/** The absolute file path associated with this image. */
char filepath[IMB_FILEPATH_SIZE];
/* For movie files, the frame number loaded from the file. */
int fileframe;
/* memory cache limiter */
/** reference counter for multiple users */
+2 -1
View File
@@ -1285,7 +1285,8 @@ ImBuf *IMB_anim_absolute(ImBufAnim *anim,
#endif
if (ibuf) {
SNPRINTF(ibuf->filepath, "%s.%04d", anim->filepath, anim->cur_position + 1);
STRNCPY(ibuf->filepath, anim->filepath);
ibuf->fileframe = anim->cur_position + 1;
}
return ibuf;
}
@@ -2016,6 +2016,7 @@ static bool wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_
#endif /* USE_FRAME_CACHE_LIMIT */
STRNCPY(ibuf->filepath, ps.picture->filepath);
ibuf->fileframe = ps.picture->frame;
}
/* NOTE(@ideasman42): why only windows? (from 2.4x). */