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
committed by Brecht Van Lommel
parent 3b6075e7c6
commit 5b1126da66
5 changed files with 23 additions and 6 deletions

View File

@@ -109,6 +109,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 */

View File

@@ -467,6 +467,7 @@ struct UndoImageBuf {
UndoImageBuf *post;
char ibuf_filepath[IMB_FILEPATH_SIZE];
int ibuf_fileframe;
UndoImageTile **tiles;
@@ -498,6 +499,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;
@@ -667,10 +669,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;
}
}
@@ -679,7 +682,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);
@@ -690,7 +693,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);
}
@@ -773,7 +776,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]) &&

View File

@@ -242,6 +242,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;
/** reference counter for multiple users */
int32_t refcounter;

View File

@@ -1432,7 +1432,8 @@ ImBuf *MOV_decode_frame(MovieReader *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;
}

View File

@@ -2015,6 +2015,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;
}
while (pupdate_time()) {