From da38cb622e0f2e6389fee3ffb148ca77b5979014 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 7 Feb 2020 00:01:50 +0100 Subject: Cleanup: split image sequence detection into own file, and make it reusable --- source/blender/editors/include/ED_image.h | 26 ++ source/blender/editors/space_image/CMakeLists.txt | 1 + source/blender/editors/space_image/image_ops.c | 274 ++------------------- .../blender/editors/space_image/image_sequence.c | 249 +++++++++++++++++++ 4 files changed, 295 insertions(+), 255 deletions(-) create mode 100644 source/blender/editors/space_image/image_sequence.c (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index e6d8684a8b2..d78c2620253 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -24,15 +24,21 @@ #ifndef __ED_IMAGE_H__ #define __ED_IMAGE_H__ +#include "DNA_listBase.h" +#include "DNA_space_types.h" + struct ARegion; struct ImBuf; struct Image; struct ImageUser; +struct LinkNodePair; +struct Main; struct ReportList; struct Scene; struct SpaceImage; struct ViewLayer; struct bContext; +struct wmOperator; struct wmWindowManager; /* image_edit.c, exported for transform */ @@ -116,4 +122,24 @@ bool ED_image_should_save_modified(const struct bContext *C); int ED_image_save_all_modified_info(const struct bContext *C, struct ReportList *reports); bool ED_image_save_all_modified(const struct bContext *C, struct ReportList *reports); +/* image_sequence.c */ +typedef struct ImageFrameRange { + struct ImageFrameRange *next, *prev; + + /** Absolute file path of the first file in the range. */ + char filepath[FILE_MAX]; + /* Sequence parameters. */ + int length; + int offset; + /* UDIM tiles. */ + ListBase udim_tiles; + + /* Temporary data. */ + ListBase frames; +} ImageFrameRange; + +ListBase ED_image_filesel_detect_sequences(struct Main *bmain, + struct wmOperator *op, + const bool detect_udim); + #endif /* __ED_IMAGE_H__ */ diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index 5abcff436f1..12de74c6ae7 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC image_draw.c image_edit.c image_ops.c + image_sequence.c image_undo.c space_image.c diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index a3fa03b18b7..fe1cb1ead9e 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1207,18 +1207,6 @@ typedef struct ImageOpenData { ImageFormatData im_format; } ImageOpenData; -typedef struct ImageFrameRange { - struct ImageFrameRange *next, *prev; - ListBase frames; - /** The full path of the first file in the list of image files */ - char filepath[FILE_MAX]; -} ImageFrameRange; - -typedef struct ImageFrame { - struct ImageFrame *next, *prev; - int framenr; -} ImageFrame; - static void image_open_init(bContext *C, wmOperator *op) { ImageOpenData *iod; @@ -1233,179 +1221,18 @@ static void image_open_cancel(bContext *UNUSED(C), wmOperator *op) op->customdata = NULL; } -/** - * Get a list of frames from the list of image files matching the first file name sequence pattern. - * \param ptr[in]: The RNA pointer containing the "directory" entry and "files" collection. - * \param frames_all[out]: the list of frame numbers found in the files matching - * the first one by name. - */ -static void image_sequence_get_frame_ranges(PointerRNA *ptr, ListBase *frames_all) -{ - char dir[FILE_MAXDIR]; - const bool do_frame_range = RNA_boolean_get(ptr, "use_sequence_detection"); - ImageFrameRange *frame_range = NULL; - - RNA_string_get(ptr, "directory", dir); - RNA_BEGIN (ptr, itemptr, "files") { - char base_head[FILE_MAX], base_tail[FILE_MAX]; - char head[FILE_MAX], tail[FILE_MAX]; - unsigned short digits; - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); - ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame"); - - /* use the first file in the list as base filename */ - frame->framenr = BLI_stringdec(filename, head, tail, &digits); - - /* still in the same sequence */ - if (do_frame_range && (frame_range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) && - (STREQLEN(base_tail, tail, FILE_MAX))) { - /* pass */ - } - else { - /* start a new frame range */ - frame_range = MEM_callocN(sizeof(*frame_range), __func__); - BLI_join_dirfile(frame_range->filepath, sizeof(frame_range->filepath), dir, filename); - BLI_addtail(frames_all, frame_range); - - BLI_strncpy(base_head, head, sizeof(base_head)); - BLI_strncpy(base_tail, tail, sizeof(base_tail)); - } - - BLI_addtail(&frame_range->frames, frame); - MEM_freeN(filename); - } - RNA_END; -} - -static int image_cmp_frame(const void *a, const void *b) -{ - const ImageFrame *frame_a = a; - const ImageFrame *frame_b = b; - - if (frame_a->framenr < frame_b->framenr) { - return -1; - } - if (frame_a->framenr > frame_b->framenr) { - return 1; - } - return 0; -} - -/* Checks whether the given filepath refers to a UDIM texture. - * If yes, the range from 1001 to the highest tile is returned, otherwise 0. - * - * If the result is positive, the filepath will be overwritten with that of - * the 1001 tile. - * udim_tiles may get filled even if the result ultimately is false! */ -static int image_get_udim(char *filepath, LinkNodePair *udim_tiles) -{ - char filename[FILE_MAX], dirname[FILE_MAXDIR]; - BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); - - unsigned short digits; - char base_head[FILE_MAX], base_tail[FILE_MAX]; - int id = BLI_stringdec(filename, base_head, base_tail, &digits); - - if (id < 1001 || id >= IMA_UDIM_MAX) { - return 0; - } - - bool is_udim = true; - bool has_primary = false; - int max_udim = 0; - - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(dirname, &dir); - for (int i = 0; i < totfile; i++) { - if (!(dir[i].type & S_IFREG)) { - continue; - } - char head[FILE_MAX], tail[FILE_MAX]; - id = BLI_stringdec(dir[i].relname, head, tail, &digits); - - if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) || - !(STREQLEN(base_tail, tail, FILE_MAX))) { - continue; - } - - if (id < 1001 || id >= IMA_UDIM_MAX) { - is_udim = false; - break; - } - if (id == 1001) { - has_primary = true; - } - - BLI_linklist_append(udim_tiles, POINTER_FROM_INT(id)); - max_udim = max_ii(max_udim, id); - } - BLI_filelist_free(dir, totfile); - - if (is_udim && has_primary) { - char primary_filename[FILE_MAX]; - BLI_stringenc(primary_filename, base_head, base_tail, digits, 1001); - BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename); - return max_udim - 1000; - } - return 0; -} - -/** - * Return the start (offset) and the length of the sequence of - * continuous frames in the list of frames. - * - * \param frames: [in] the list of frame numbers, as a side-effect the list is sorted. - * \param ofs: [out] offset the first frame number in the sequence. - * \return the number of contiguous frames in the sequence - */ -static int image_sequence_get_len(ImageFrameRange *frame_range, - int *ofs, - char *filepath_range, - LinkNodePair *udim_tiles) -{ - ImageFrame *frame; - - BLI_listbase_sort(&frame_range->frames, image_cmp_frame); - BLI_strncpy(filepath_range, frame_range->filepath, FILE_MAX); - - frame = frame_range->frames.first; - if (frame != NULL) { - int frame_curr = frame->framenr; - (*ofs) = frame_curr; - - if (udim_tiles != NULL) { - int len_udim = image_get_udim(filepath_range, udim_tiles); - if (len_udim > 0) { - *ofs = 1001; - return len_udim; - } - } - - while (frame != NULL && (frame->framenr == frame_curr)) { - frame_curr++; - frame = frame->next; - } - return frame_curr - (*ofs); - } - *ofs = 0; - return 0; -} - static Image *image_open_single(Main *bmain, wmOperator *op, - const char *filepath, + ImageFrameRange *range, const char *relbase, bool is_relative_path, - bool use_multiview, - int frame_seq_len, - int frame_seq_ofs, - LinkNodePair *udim_tiles) + bool use_multiview) { bool exists = false; Image *ima = NULL; errno = 0; - ima = BKE_image_load_exists_ex(bmain, filepath, &exists); + ima = BKE_image_load_exists_ex(bmain, range->filepath, &exists); if (!ima) { if (op->customdata) { @@ -1414,7 +1241,7 @@ static Image *image_open_single(Main *bmain, BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s", - filepath, + range->filepath, errno ? strerror(errno) : TIP_("unsupported image format")); return NULL; } @@ -1439,11 +1266,11 @@ static Image *image_open_single(Main *bmain, BKE_image_free_views(ima); } - if ((frame_seq_len > 1) && (ima->source == IMA_SRC_FILE)) { - if (udim_tiles && frame_seq_ofs == 1001) { + if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) { + if (range->udim_tiles.first && range->offset == 1001) { ima->source = IMA_SRC_TILED; - for (LinkNode *node = udim_tiles->list; node; node = node->next) { - BKE_image_add_tile(ima, POINTER_AS_INT(node->link), NULL); + for (LinkData *node = range->udim_tiles.first; node; node = node->next) { + BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL); } } else { @@ -1464,7 +1291,6 @@ static int image_open_exec(bContext *C, wmOperator *op) ImageUser *iuser = NULL; ImageOpenData *iod = op->customdata; Image *ima = NULL; - char filepath[FILE_MAX]; int frame_seq_len = 0; int frame_ofs = 1; @@ -1476,83 +1302,21 @@ static int image_open_exec(bContext *C, wmOperator *op) image_open_init(C, op); } - RNA_string_get(op->ptr, "filepath", filepath); - - if (RNA_struct_property_is_set(op->ptr, "directory") && - RNA_struct_property_is_set(op->ptr, "files")) { - bool was_relative = BLI_path_is_rel(filepath); - ListBase frame_ranges_all; - - BLI_listbase_clear(&frame_ranges_all); - image_sequence_get_frame_ranges(op->ptr, &frame_ranges_all); - for (ImageFrameRange *frame_range = frame_ranges_all.first; frame_range; - frame_range = frame_range->next) { - int frame_range_ofs; - - LinkNodePair udim_tiles = {NULL}; - LinkNodePair *udim_tiles_ptr = use_udim ? (&udim_tiles) : NULL; - - char filepath_range[FILE_MAX]; - int frame_range_seq_len = image_sequence_get_len( - frame_range, &frame_range_ofs, filepath_range, udim_tiles_ptr); - BLI_freelistN(&frame_range->frames); - - if (was_relative) { - BLI_path_rel(filepath_range, BKE_main_blendfile_path(bmain)); - } - - Image *ima_range = image_open_single(bmain, - op, - filepath_range, - BKE_main_blendfile_path(bmain), - is_relative_path, - use_multiview, - frame_range_seq_len, - frame_range_ofs, - udim_tiles_ptr); - - /* take the first image */ - if ((ima == NULL) && ima_range) { - ima = ima_range; - frame_seq_len = frame_range_seq_len; - frame_ofs = frame_range_ofs; - } + ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim); + for (ImageFrameRange *range = ranges.first; range; range = range->next) { + Image *ima_range = image_open_single( + bmain, op, range, BKE_main_blendfile_path(bmain), is_relative_path, use_multiview); - BLI_linklist_free(udim_tiles.list, NULL); + /* take the first image */ + if ((ima == NULL) && ima_range) { + ima = ima_range; + frame_seq_len = range->length; + frame_ofs = range->offset; } - BLI_freelistN(&frame_ranges_all); - } - else { - /* for drag & drop etc. */ - - LinkNodePair udim_tiles = {NULL}; - frame_seq_len = 1; - char filepath_range[FILE_MAX]; - BLI_strncpy(filepath_range, filepath, FILE_MAX); - - if (use_udim > 0) { - /* Try to find UDIM tiles corresponding to the image */ - int udim_len = image_get_udim(filepath_range, &udim_tiles); - - /* If we found something, mark the image as tiled. */ - if (udim_len) { - frame_seq_len = udim_len; - frame_ofs = 1001; - } - } - - ima = image_open_single(bmain, - op, - filepath_range, - BKE_main_blendfile_path(bmain), - is_relative_path, - use_multiview, - frame_seq_len, - frame_ofs, - &udim_tiles); - BLI_linklist_free(udim_tiles.list, NULL); + BLI_freelistN(&range->udim_tiles); } + BLI_freelistN(&ranges); if (ima == NULL) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c new file mode 100644 index 00000000000..09e717621d8 --- /dev/null +++ b/source/blender/editors/space_image/image_sequence.c @@ -0,0 +1,249 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup spimage + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_fileops.h" +#include "BLI_fileops_types.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_image_types.h" +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" + +#include "RNA_access.h" + +#include "BKE_image.h" +#include "BKE_main.h" + +#include "ED_image.h" + +#include "WM_types.h" + +typedef struct ImageFrame { + struct ImageFrame *next, *prev; + int framenr; +} ImageFrame; + +/** + * Get a list of frames from the list of image files matching the first file + * name sequence pattern. The files and directory are read from standard + * fileselect operator properties. + * + * The output is a list of frame ranges, each containg a list of frames with + * matching names. + */ +static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) +{ + char dir[FILE_MAXDIR]; + const bool do_frame_range = RNA_boolean_get(op->ptr, "use_sequence_detection"); + ImageFrameRange *range = NULL; + + RNA_string_get(op->ptr, "directory", dir); + RNA_BEGIN (op->ptr, itemptr, "files") { + char base_head[FILE_MAX], base_tail[FILE_MAX]; + char head[FILE_MAX], tail[FILE_MAX]; + unsigned short digits; + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame"); + + /* use the first file in the list as base filename */ + frame->framenr = BLI_stringdec(filename, head, tail, &digits); + + /* still in the same sequence */ + if (do_frame_range && (range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) && + (STREQLEN(base_tail, tail, FILE_MAX))) { + /* pass */ + } + else { + /* start a new frame range */ + range = MEM_callocN(sizeof(*range), __func__); + BLI_join_dirfile(range->filepath, sizeof(range->filepath), dir, filename); + BLI_addtail(ranges, range); + + BLI_strncpy(base_head, head, sizeof(base_head)); + BLI_strncpy(base_tail, tail, sizeof(base_tail)); + } + + BLI_addtail(&range->frames, frame); + MEM_freeN(filename); + } + RNA_END; +} + +static int image_cmp_frame(const void *a, const void *b) +{ + const ImageFrame *frame_a = a; + const ImageFrame *frame_b = b; + + if (frame_a->framenr < frame_b->framenr) { + return -1; + } + if (frame_a->framenr > frame_b->framenr) { + return 1; + } + return 0; +} + +/* + * Checks whether the given filepath refers to a UDIM texture. + * If yes, the range from 1001 to the highest tile is returned, otherwise 0. + * + * If the result is positive, the filepath will be overwritten with that of + * the 1001 tile. + * + * udim_tiles may get filled even if the result ultimately is false! + */ +static int image_get_udim(char *filepath, ListBase *udim_tiles) +{ + char filename[FILE_MAX], dirname[FILE_MAXDIR]; + BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); + + unsigned short digits; + char base_head[FILE_MAX], base_tail[FILE_MAX]; + int id = BLI_stringdec(filename, base_head, base_tail, &digits); + + if (id < 1001 || id >= IMA_UDIM_MAX) { + return 0; + } + + bool is_udim = true; + bool has_primary = false; + int max_udim = 0; + + struct direntry *dir; + uint totfile = BLI_filelist_dir_contents(dirname, &dir); + for (int i = 0; i < totfile; i++) { + if (!(dir[i].type & S_IFREG)) { + continue; + } + char head[FILE_MAX], tail[FILE_MAX]; + id = BLI_stringdec(dir[i].relname, head, tail, &digits); + + if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) || + !(STREQLEN(base_tail, tail, FILE_MAX))) { + continue; + } + + if (id < 1001 || id >= IMA_UDIM_MAX) { + is_udim = false; + break; + } + if (id == 1001) { + has_primary = true; + } + + BLI_addtail(udim_tiles, BLI_genericNodeN(POINTER_FROM_INT(id))); + max_udim = max_ii(max_udim, id); + } + BLI_filelist_free(dir, totfile); + + if (is_udim && has_primary) { + char primary_filename[FILE_MAX]; + BLI_stringenc(primary_filename, base_head, base_tail, digits, 1001); + BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename); + return max_udim - 1000; + } + return 0; +} + +/** + * From a list of frames, compute the start (offset) and length of the sequence + * of contiguous frames. If UDIM is detect, it will return UDIM tiles as well. + */ +static void image_detect_frame_range(ImageFrameRange *range, const bool detect_udim) +{ + /* UDIM */ + if (detect_udim) { + int len_udim = image_get_udim(range->filepath, &range->udim_tiles); + + if (len_udim > 0) { + range->offset = 1001; + range->length = len_udim; + return; + } + } + + /* Image Sequence */ + BLI_listbase_sort(&range->frames, image_cmp_frame); + + ImageFrame *frame = range->frames.first; + if (frame != NULL) { + int frame_curr = frame->framenr; + range->offset = frame_curr; + + while (frame != NULL && (frame->framenr == frame_curr)) { + frame_curr++; + frame = frame->next; + } + + range->length = frame_curr - range->offset; + } + else { + range->length = 1; + range->offset = 0; + } +} + +/* Used for both images and volume file loading. */ +ListBase ED_image_filesel_detect_sequences(Main *bmain, wmOperator *op, const bool detect_udim) +{ + ListBase ranges; + BLI_listbase_clear(&ranges); + + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + /* File browser. */ + if (RNA_struct_property_is_set(op->ptr, "directory") && + RNA_struct_property_is_set(op->ptr, "files")) { + const bool was_relative = BLI_path_is_rel(filepath); + + image_sequence_get_frame_ranges(op, &ranges); + for (ImageFrameRange *range = ranges.first; range; range = range->next) { + image_detect_frame_range(range, detect_udim); + BLI_freelistN(&range->frames); + + if (was_relative) { + BLI_path_rel(range->filepath, BKE_main_blendfile_path(bmain)); + } + } + } + /* Filepath property for drag & drop etc. */ + else { + ImageFrameRange *range = MEM_callocN(sizeof(*range), __func__); + BLI_addtail(&ranges, range); + + BLI_strncpy(range->filepath, filepath, FILE_MAX); + image_detect_frame_range(range, detect_udim); + } + + return ranges; +} -- cgit v1.2.3