Cleanup: Move BKE_node.h to C++ #107790
|
@ -126,6 +126,7 @@ bool OneapiDevice::can_use_hardware_raytracing_for_features(uint requested_featu
|
|||
# if defined(RTC_VERSION) && RTC_VERSION < 40100
|
||||
return !(requested_features & (KERNEL_FEATURE_MNEE | KERNEL_FEATURE_NODE_RAYTRACE));
|
||||
# else
|
||||
(void)requested_features;
|
||||
return true;
|
||||
# endif
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ from bpy.app.translations import pgettext_tip as tip_
|
|||
|
||||
|
||||
class ANIM_OT_keying_set_export(Operator):
|
||||
"""Export Keying Set to a python script"""
|
||||
"""Export Keying Set to a Python script"""
|
||||
bl_idname = "anim.keying_set_export"
|
||||
bl_label = "Export Keying Set..."
|
||||
|
||||
|
@ -38,7 +38,7 @@ class ANIM_OT_keying_set_export(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ def _lang_module_get(sc):
|
|||
|
||||
|
||||
class ConsoleExec(Operator):
|
||||
"""Execute the current console line as a python expression"""
|
||||
"""Execute the current console line as a Python expression"""
|
||||
bl_idname = "console.execute"
|
||||
bl_label = "Console Execute"
|
||||
bl_options = {'UNDO_GROUPED'}
|
||||
|
|
|
@ -78,7 +78,7 @@ class WM_OT_previews_batch_generate(Operator):
|
|||
use_trusted: BoolProperty(
|
||||
default=False,
|
||||
name="Trusted Blend Files",
|
||||
description="Enable python evaluation for selected files",
|
||||
description="Enable Python evaluation for selected files",
|
||||
)
|
||||
use_backups: BoolProperty(
|
||||
default=True,
|
||||
|
@ -188,7 +188,7 @@ class WM_OT_previews_batch_clear(Operator):
|
|||
use_trusted: BoolProperty(
|
||||
default=False,
|
||||
name="Trusted Blend Files",
|
||||
description="Enable python evaluation for selected files",
|
||||
description="Enable Python evaluation for selected files",
|
||||
)
|
||||
use_backups: BoolProperty(
|
||||
default=True,
|
||||
|
|
|
@ -173,7 +173,7 @@ class PREFERENCES_OT_keyconfig_test(Operator):
|
|||
|
||||
|
||||
class PREFERENCES_OT_keyconfig_import(Operator):
|
||||
"""Import key configuration from a python script"""
|
||||
"""Import key configuration from a Python script"""
|
||||
bl_idname = "preferences.keyconfig_import"
|
||||
bl_label = "Import Key Configuration..."
|
||||
|
||||
|
@ -192,7 +192,7 @@ class PREFERENCES_OT_keyconfig_import(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
@ -244,7 +244,7 @@ class PREFERENCES_OT_keyconfig_import(Operator):
|
|||
|
||||
|
||||
class PREFERENCES_OT_keyconfig_export(Operator):
|
||||
"""Export key configuration to a python script"""
|
||||
"""Export key configuration to a Python script"""
|
||||
bl_idname = "preferences.keyconfig_export"
|
||||
bl_label = "Export Key Configuration..."
|
||||
|
||||
|
@ -268,7 +268,7 @@ class PREFERENCES_OT_keyconfig_export(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
@ -610,7 +610,7 @@ class PREFERENCES_OT_addon_install(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
|
|
@ -746,7 +746,7 @@ class WM_OT_operator_pie_enum(Operator):
|
|||
|
||||
data_path: StringProperty(
|
||||
name="Operator",
|
||||
description="Operator name (in python as string)",
|
||||
description="Operator name (in Python as string)",
|
||||
maxlen=1024,
|
||||
)
|
||||
prop_string: StringProperty(
|
||||
|
@ -1381,7 +1381,7 @@ rna_custom_property_type_items = (
|
|||
('BOOL', "Boolean", "A true or false value"),
|
||||
('BOOL_ARRAY', "Boolean Array", "An array of true or false values"),
|
||||
('STRING', "String", "A string value"),
|
||||
('PYTHON', "Python", "Edit a python value directly, for unsupported property types"),
|
||||
('PYTHON', "Python", "Edit a Python value directly, for unsupported property types"),
|
||||
)
|
||||
|
||||
rna_custom_property_subtype_none_item = ('NONE', "Plain Data", "Data values without special behavior")
|
||||
|
|
|
@ -831,7 +831,7 @@ class ConstraintButtonsPanel:
|
|||
|
||||
def draw_python_constraint(self, _context):
|
||||
layout = self.layout
|
||||
layout.label(text="Blender 2.6 doesn't support python constraints yet")
|
||||
layout.label(text="Blender 2.6 doesn't support Python constraints yet")
|
||||
|
||||
def draw_armature(self, context):
|
||||
layout = self.layout
|
||||
|
|
|
@ -192,6 +192,7 @@ class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, Panel):
|
|||
|
||||
if itasc:
|
||||
layout.prop(itasc, "mode")
|
||||
layout.prop(itasc, "translate_root_bones")
|
||||
simulation = (itasc.mode == 'SIMULATION')
|
||||
if simulation:
|
||||
layout.prop(itasc, "reiteration_method", expand=False)
|
||||
|
|
|
@ -25,13 +25,13 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 8
|
||||
#define BLENDER_FILE_SUBVERSION 9
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
* was written with too new a version. */
|
||||
#define BLENDER_FILE_MIN_VERSION 305
|
||||
#define BLENDER_FILE_MIN_SUBVERSION 10
|
||||
#define BLENDER_FILE_MIN_SUBVERSION 9
|
||||
|
||||
/** User readable version string. */
|
||||
const char *BKE_blender_version_string(void);
|
||||
|
|
|
@ -1666,6 +1666,10 @@ void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_onl
|
|||
id->tag &= ~(LIB_TAG_EXTRAUSER | LIB_TAG_EXTRAUSER_SET);
|
||||
id_us_ensure_real(id);
|
||||
}
|
||||
if (ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS)) {
|
||||
/* These IDs should always have a 'virtual' user. */
|
||||
id_us_ensure_real(id);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
|
|
|
@ -949,12 +949,12 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
|
|||
if (ob->id.lib) {
|
||||
BLO_reportf_wrap(reports,
|
||||
RPT_INFO,
|
||||
TIP_("Can't find object data of %s lib %s\n"),
|
||||
TIP_("Can't find object data of %s lib %s"),
|
||||
ob->id.name + 2,
|
||||
ob->id.lib->filepath);
|
||||
}
|
||||
else {
|
||||
BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2);
|
||||
BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data"), ob->id.name + 2);
|
||||
}
|
||||
reports->count.missing_obdata++;
|
||||
}
|
||||
|
|
|
@ -164,13 +164,13 @@ static void filepath_avi(char *filepath, const RenderData *rd, bool preview, con
|
|||
|
||||
if (rd->scemode & R_EXTENSION) {
|
||||
if (!BLI_path_extension_check(filepath, ".avi")) {
|
||||
BLI_path_frame_range(filepath, sfra, efra, 4);
|
||||
BLI_path_frame_range(filepath, FILE_MAX, sfra, efra, 4);
|
||||
BLI_strncat(filepath, ".avi", FILE_MAX);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (BLI_path_frame_check_chars(filepath)) {
|
||||
BLI_path_frame_range(filepath, sfra, efra, 4);
|
||||
BLI_path_frame_range(filepath, FILE_MAX, sfra, efra, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1406,7 +1406,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context,
|
|||
if (*fe == NULL) {
|
||||
BLI_strncat(string, autosplit, FILE_MAX);
|
||||
|
||||
BLI_path_frame_range(string, sfra, efra, 4);
|
||||
BLI_path_frame_range(string, FILE_MAX, sfra, efra, 4);
|
||||
BLI_strncat(string, *exts, FILE_MAX);
|
||||
}
|
||||
else {
|
||||
|
@ -1417,7 +1417,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context,
|
|||
}
|
||||
else {
|
||||
if (BLI_path_frame_check_chars(string)) {
|
||||
BLI_path_frame_range(string, sfra, efra, 4);
|
||||
BLI_path_frame_range(string, FILE_MAX, sfra, efra, 4);
|
||||
}
|
||||
|
||||
BLI_strncat(string, autosplit, FILE_MAX);
|
||||
|
|
|
@ -15,27 +15,219 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Queries
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Sets the specified environment variable to the specified value,
|
||||
* and clears it if `val == NULL`.
|
||||
*/
|
||||
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Only set an environment variable if already not there.
|
||||
* Like Unix `setenv(env, val, 0);`
|
||||
* Get an element of the path at an index, eg:
|
||||
* `/some/path/file.txt` where an index of:
|
||||
* - 0 or -3: `some`
|
||||
* - 1 or -2: `path`
|
||||
* - 2 or -1: `file.txt`
|
||||
*
|
||||
* (not used anywhere).
|
||||
* Ignored elements in the path:
|
||||
* - Multiple slashes at any point in the path (including start/end).
|
||||
* - Single '.' in the path: `/./` except for the beginning of the path
|
||||
* where it's used to signify a $PWD relative path.
|
||||
*/
|
||||
void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
bool BLI_path_name_at_index(const char *__restrict path,
|
||||
int index,
|
||||
int *__restrict r_offset,
|
||||
int *__restrict r_len) ATTR_NONNULL(1, 3, 4) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Get an environment variable, result has to be used immediately.
|
||||
*
|
||||
* On windows #getenv gets its variables from a static copy of the environment variables taken at
|
||||
* process start-up, causing it to not pick up on environment variables created during runtime.
|
||||
* This function uses an alternative method to get environment variables that does pick up on
|
||||
* runtime environment variables. The result will be UTF-8 encoded.
|
||||
* Return true if the path is a UNC share.
|
||||
*/
|
||||
const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Parent Operations
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Go back one directory.
|
||||
*
|
||||
* Replaces path with the path of its parent directory, returning true if
|
||||
* it was able to find a parent directory within the path.
|
||||
*
|
||||
* On success, the resulting path will always have a trailing slash.
|
||||
*/
|
||||
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Go back until the directory is found.
|
||||
*
|
||||
* Strips off nonexistent (or non-accessible) sub-directories from the end of `dir`,
|
||||
* leaving the path of the lowest-level directory that does exist and we can read.
|
||||
*/
|
||||
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* In the simple case this is similar to `BLI_path_slash_rfind(dirname)`
|
||||
* however it behaves differently when there are redundant characters:
|
||||
*
|
||||
* `/test///dir/./file`
|
||||
* ^
|
||||
* `/test/dir/subdir//file`
|
||||
* ^
|
||||
* \return The position after the parent paths last character or NULL on failure.
|
||||
* Neither `path` or `&path[path_len - 1]` are ever returned.
|
||||
*/
|
||||
const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Make Safe / Sanitize
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Make given name safe to be used in paths.
|
||||
*
|
||||
* \param allow_tokens: Permit the usage of '<' and '>' characters. This can be
|
||||
* leveraged by higher layers to support "virtual filenames" which contain
|
||||
* substitution markers delineated between the two characters.
|
||||
*
|
||||
* \return true if \a fname was changed, false otherwise.
|
||||
*
|
||||
* For now, simply replaces reserved chars (as listed in
|
||||
* https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words )
|
||||
* by underscores ('_').
|
||||
*
|
||||
* \note Space case ' ' is a bit of an edge case here - in theory it is allowed,
|
||||
* but again can be an issue in some cases, so we simply replace it by an underscore too
|
||||
* (good practice anyway).
|
||||
* REMOVED based on popular demand (see #45900).
|
||||
* Percent '%' char is a bit same case - not recommended to use it,
|
||||
* but supported by all decent file-systems/operating-systems around.
|
||||
*
|
||||
* \note On Windows, it also ensures there is no '.' (dot char) at the end of the file,
|
||||
* this can lead to issues.
|
||||
*
|
||||
* \note On Windows, it also checks for forbidden names
|
||||
* (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx ).
|
||||
*/
|
||||
bool BLI_path_make_safe_filename_ex(char *fname, bool allow_tokens) ATTR_NONNULL(1);
|
||||
bool BLI_path_make_safe_filename(char *fname) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Make given path OS-safe.
|
||||
*
|
||||
* \return true if \a path was changed, false otherwise.
|
||||
*/
|
||||
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Creates a display string from path to be used menus and the user interface.
|
||||
* Like `bpy.path.display_name()`.
|
||||
*/
|
||||
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Normalize
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Remove redundant characters from \a path.
|
||||
*
|
||||
* The following operations are performed:
|
||||
* - Redundant path components such as `//`, `/./` & `./` (prefix) are stripped.
|
||||
* (with the exception of `//` prefix used for blend-file relative paths).
|
||||
* - `..` are resolved so `<parent>/../<child>/` resolves to `<child>/`.
|
||||
* Note that the resulting path may begin with `..` if it's relative.
|
||||
*
|
||||
* Details:
|
||||
* - The slash direction is expected to be native (see #SEP).
|
||||
* When calculating a canonical paths you may need to run #BLI_path_slash_native first.
|
||||
* #BLI_path_cmp_normalized can be used for canonical path comparison.
|
||||
* - Trailing slashes are left intact (unlike Python which strips them).
|
||||
* - Handling paths beginning with `..` depends on them being absolute or relative.
|
||||
* For absolute paths they are removed (e.g. `/../path` becomes `/path`).
|
||||
* For relative paths they are kept as it's valid to reference paths above a relative location
|
||||
* such as `//../parent` or `../parent`.
|
||||
*
|
||||
* \param path: The path to a file or directory which can be absolute or relative.
|
||||
*/
|
||||
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Cleanup file-path ensuring a trailing slash.
|
||||
*
|
||||
* \note Same as #BLI_path_normalize but adds a trailing slash.
|
||||
*/
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxncpy) ATTR_NONNULL(1);
|
||||
|
||||
#if defined(WIN32)
|
||||
void BLI_path_normalize_unc_16(wchar_t *path_16);
|
||||
void BLI_path_normalize_unc(char *path, int path_maxncpy);
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path FileName Manipulation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Ensure `filepath` has a file component, adding `filename` when it's empty or ends with a slash.
|
||||
* \return true if the `filename` was appended to `filepath`.
|
||||
*/
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
/**
|
||||
* Appends a suffix to the `path`, fitting it before the extension
|
||||
*
|
||||
* path = `Foo.png`, suffix = `123`, separator = `_`.
|
||||
* `Foo.png` -> `Foo_123.png`.
|
||||
*
|
||||
* \param path: original (and final) string.
|
||||
* \param path_maxncpy: Maximum length of path.
|
||||
* \param suffix: String to append to the original path.
|
||||
* \param sep: Optional separator character.
|
||||
* \return true if succeeded.
|
||||
*/
|
||||
bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep)
|
||||
ATTR_NONNULL(1, 3, 4);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Slash Utilities
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \return pointer to the leftmost path separator in path (or NULL when not found).
|
||||
*/
|
||||
const char *BLI_path_slash_find(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* \return pointer to the rightmost path separator in path (or NULL when not found).
|
||||
*/
|
||||
const char *BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Appends a slash to path if there isn't one there already.
|
||||
* Returns the new length of the path.
|
||||
*/
|
||||
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Removes the last slash and everything after it to the end of path, if there is one.
|
||||
*/
|
||||
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Changes to the path separators to the native ones for this OS.
|
||||
*/
|
||||
void BLI_path_slash_native(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Directory/FileName Split
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Copies directory and file components from `filepath` into `dir` and `file`, e.g.
|
||||
|
@ -58,17 +250,20 @@ void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy
|
|||
*/
|
||||
void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy)
|
||||
ATTR_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns a pointer to the nil byte when no extension is found.
|
||||
* Like Python's `os.path.basename()`
|
||||
*
|
||||
* \return The pointer into \a path string immediately after last slash,
|
||||
* or start of \a path if none found.
|
||||
*/
|
||||
const char *BLI_path_extension_or_end(const char *filepath)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
|
||||
/**
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns NULL if there is no extension.
|
||||
*/
|
||||
const char *BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
const char *BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Append
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Append a filename to a dir, ensuring slash separates.
|
||||
|
@ -83,6 +278,12 @@ size_t BLI_path_append(char *__restrict dst, size_t dst_maxncpy, const char *__r
|
|||
size_t BLI_path_append_dir(char *__restrict dst, size_t dst_maxncpy, const char *__restrict dir)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Join
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* See #BLI_path_join doc-string.
|
||||
*/
|
||||
|
@ -192,65 +393,23 @@ BLI_INLINE size_t _BLI_path_join_12(_BLI_PATH_JOIN_ARGS_10)
|
|||
#undef _BLI_PATH_JOIN_ARGS_9
|
||||
#undef _BLI_PATH_JOIN_ARGS_10
|
||||
|
||||
/**
|
||||
* Like Python's `os.path.basename()`
|
||||
*
|
||||
* \return The pointer into \a path string immediately after last slash,
|
||||
* or start of \a path if none found.
|
||||
*/
|
||||
const char *BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Get an element of the path at an index, eg:
|
||||
* `/some/path/file.txt` where an index of:
|
||||
* - 0 or -3: `some`
|
||||
* - 1 or -2: `path`
|
||||
* - 2 or -1: `file.txt`
|
||||
*
|
||||
* Ignored elements in the path:
|
||||
* - Multiple slashes at any point in the path (including start/end).
|
||||
* - Single '.' in the path: `/./` except for the beginning of the path
|
||||
* where it's used to signify a $PWD relative path.
|
||||
*/
|
||||
bool BLI_path_name_at_index(const char *__restrict path,
|
||||
int index,
|
||||
int *__restrict r_offset,
|
||||
int *__restrict r_len) ATTR_NONNULL(1, 3, 4) ATTR_WARN_UNUSED_RESULT;
|
||||
/** \} */
|
||||
|
||||
/** Return true only if #containee_path is contained in #container_path. */
|
||||
bool BLI_path_contains(const char *container_path, const char *containee_path)
|
||||
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path File Extensions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \return pointer to the leftmost path separator in path (or NULL when not found).
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns a pointer to the nil byte when no extension is found.
|
||||
*/
|
||||
const char *BLI_path_slash_find(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
const char *BLI_path_extension_or_end(const char *filepath)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
|
||||
/**
|
||||
* \return pointer to the rightmost path separator in path (or NULL when not found).
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns NULL if there is no extension.
|
||||
*/
|
||||
const char *BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Appends a slash to path if there isn't one there already.
|
||||
* Returns the new length of the path.
|
||||
*/
|
||||
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Removes the last slash and everything after it to the end of path, if there is one.
|
||||
*/
|
||||
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Changes to the path separators to the native ones for this OS.
|
||||
*/
|
||||
void BLI_path_slash_native(char *path) ATTR_NONNULL(1);
|
||||
|
||||
#ifdef _WIN32
|
||||
bool BLI_path_program_extensions_add_win32(char *program_name, size_t program_name_maxncpy);
|
||||
#endif
|
||||
/**
|
||||
* Search for a binary (executable)
|
||||
*/
|
||||
bool BLI_path_program_search(char *program_filepath,
|
||||
size_t program_filepath_maxncpy,
|
||||
const char *program_name) ATTR_NONNULL(1, 3);
|
||||
const char *BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* \return true when `path` end with `ext` (case insensitive).
|
||||
|
@ -295,230 +454,12 @@ bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1);
|
|||
*/
|
||||
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
|
||||
ATTR_NONNULL(1, 3);
|
||||
/**
|
||||
* Ensure `filepath` has a file component, adding `filename` when it's empty or ends with a slash.
|
||||
* \return true if the `filename` was appended to `filepath`.
|
||||
*/
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
|
||||
ATTR_NONNULL(1, 3);
|
||||
/**
|
||||
* Looks for a sequence of decimal digits in `path`, preceding any filename extension,
|
||||
* returning the integer value if found, or 0 if not.
|
||||
*
|
||||
* \param path: String to scan.
|
||||
* \param head: Optional area to return copy of part of `path` prior to digits,
|
||||
* or before dot if no digits.
|
||||
* \param tail: Optional area to return copy of part of `path` following digits,
|
||||
* or from dot if no digits.
|
||||
* \param r_digits_len: Optional to return number of digits found.
|
||||
*/
|
||||
int BLI_path_sequence_decode(const char *path,
|
||||
char *head,
|
||||
size_t head_maxncpy,
|
||||
char *tail,
|
||||
size_t tail_maxncpy,
|
||||
unsigned short *r_digits_len);
|
||||
/**
|
||||
* Returns in area pointed to by `path` a string of the form `<head><pic><tail>`,
|
||||
* where pic is formatted as `numlen` digits with leading zeroes.
|
||||
*/
|
||||
void BLI_path_sequence_encode(char *path,
|
||||
size_t path_maxncpy,
|
||||
const char *head,
|
||||
const char *tail,
|
||||
unsigned short numlen,
|
||||
int pic);
|
||||
|
||||
/**
|
||||
* Remove redundant characters from \a path.
|
||||
*
|
||||
* The following operations are performed:
|
||||
* - Redundant path components such as `//`, `/./` & `./` (prefix) are stripped.
|
||||
* (with the exception of `//` prefix used for blend-file relative paths).
|
||||
* - `..` are resolved so `<parent>/../<child>/` resolves to `<child>/`.
|
||||
* Note that the resulting path may begin with `..` if it's relative.
|
||||
*
|
||||
* Details:
|
||||
* - The slash direction is expected to be native (see #SEP).
|
||||
* When calculating a canonical paths you may need to run #BLI_path_slash_native first.
|
||||
* #BLI_path_cmp_normalized can be used for canonical path comparison.
|
||||
* - Trailing slashes are left intact (unlike Python which strips them).
|
||||
* - Handling paths beginning with `..` depends on them being absolute or relative.
|
||||
* For absolute paths they are removed (e.g. `/../path` becomes `/path`).
|
||||
* For relative paths they are kept as it's valid to reference paths above a relative location
|
||||
* such as `//../parent` or `../parent`.
|
||||
*
|
||||
* \param path: The path to a file or directory which can be absolute or relative.
|
||||
*/
|
||||
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Cleanup file-path ensuring a trailing slash.
|
||||
*
|
||||
* \note Same as #BLI_path_normalize but adds a trailing slash.
|
||||
*/
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxncpy) ATTR_NONNULL(1);
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* Make given name safe to be used in paths.
|
||||
*
|
||||
* \param allow_tokens: Permit the usage of '<' and '>' characters. This can be
|
||||
* leveraged by higher layers to support "virtual filenames" which contain
|
||||
* substitution markers delineated between the two characters.
|
||||
*
|
||||
* \return true if \a fname was changed, false otherwise.
|
||||
*
|
||||
* For now, simply replaces reserved chars (as listed in
|
||||
* https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words )
|
||||
* by underscores ('_').
|
||||
*
|
||||
* \note Space case ' ' is a bit of an edge case here - in theory it is allowed,
|
||||
* but again can be an issue in some cases, so we simply replace it by an underscore too
|
||||
* (good practice anyway).
|
||||
* REMOVED based on popular demand (see #45900).
|
||||
* Percent '%' char is a bit same case - not recommended to use it,
|
||||
* but supported by all decent file-systems/operating-systems around.
|
||||
*
|
||||
* \note On Windows, it also ensures there is no '.' (dot char) at the end of the file,
|
||||
* this can lead to issues.
|
||||
*
|
||||
* \note On Windows, it also checks for forbidden names
|
||||
* (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx ).
|
||||
*/
|
||||
bool BLI_path_make_safe_filename_ex(char *fname, bool allow_tokens) ATTR_NONNULL(1);
|
||||
bool BLI_path_make_safe_filename(char *fname) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Make given path OS-safe.
|
||||
*
|
||||
* \return true if \a path was changed, false otherwise.
|
||||
*/
|
||||
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Go back one directory.
|
||||
*
|
||||
* Replaces path with the path of its parent directory, returning true if
|
||||
* it was able to find a parent directory within the path.
|
||||
*
|
||||
* On success, the resulting path will always have a trailing slash.
|
||||
*/
|
||||
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Go back until the directory is found.
|
||||
*
|
||||
* Strips off nonexistent (or non-accessible) sub-directories from the end of `dir`,
|
||||
* leaving the path of the lowest-level directory that does exist and we can read.
|
||||
*/
|
||||
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* In the simple case this is similar to `BLI_path_slash_rfind(dirname)`
|
||||
* however it behaves differently when there are redundant characters:
|
||||
*
|
||||
* `/test///dir/./file`
|
||||
* ^
|
||||
* `/test/dir/subdir//file`
|
||||
* ^
|
||||
* \return The position after the parent paths last character or NULL on failure.
|
||||
* Neither `path` or `&path[path_len - 1]` are ever returned.
|
||||
*/
|
||||
const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* If path begins with "//", strips that and replaces it with `basepath` directory.
|
||||
*
|
||||
* \note Also converts drive-letter prefix to something more sensible
|
||||
* if this is a non-drive-letter-based system.
|
||||
*
|
||||
* \param path: The path to convert.
|
||||
* \param basepath: The directory to base relative paths with.
|
||||
* \return true if the path was relative (started with "//").
|
||||
*/
|
||||
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with frame as decimal integer, with leading zeroes as necessary, to make digits.
|
||||
*/
|
||||
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with sta and end as decimal integers, with leading zeroes as necessary, to make digits
|
||||
* digits each, with a hyphen in-between.
|
||||
*/
|
||||
bool BLI_path_frame_range(char *path, int sta, int end, int digits) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Get the frame from a filename formatted by blender's frame scheme
|
||||
*/
|
||||
bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len) ATTR_NONNULL(1, 2, 3);
|
||||
/**
|
||||
* Given a `path` with digits representing frame numbers, replace the digits with the '#'
|
||||
* character and extract the extension.
|
||||
* So: `/some/path_123.jpeg`
|
||||
* Becomes: `/some/path_###` with `r_ext` set to `.jpeg`.
|
||||
*/
|
||||
void BLI_path_frame_strip(char *path, char *r_ext, size_t ext_maxncpy) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range
|
||||
*/
|
||||
bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Checks for a relative path (ignoring Blender's "//") prefix
|
||||
* (unlike `!BLI_path_is_rel(path)`).
|
||||
* When false, #BLI_path_abs_from_cwd would expand the absolute path.
|
||||
*/
|
||||
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Checks for relative path, expanding them relative to the current working directory.
|
||||
* \returns true if the expansion was performed.
|
||||
*
|
||||
* \note Should only be called with command line paths.
|
||||
* This is _not_ something Blender's internal paths support, instead they use the "//" prefix.
|
||||
* In most cases #BLI_path_abs should be used instead.
|
||||
*/
|
||||
bool BLI_path_abs_from_cwd(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Replaces `file` with a relative version (prefixed by "//") such that #BLI_path_abs, given
|
||||
* the same `basename`, will convert it back to its original value.
|
||||
*/
|
||||
void BLI_path_rel(char *path, const char *basename) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Does path begin with the special "//" prefix that Blender uses to indicate
|
||||
* a path relative to the .blend file.
|
||||
*/
|
||||
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Return true if the path is a UNC share.
|
||||
*/
|
||||
bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Creates a display string from path to be used menus and the user interface.
|
||||
* Like `bpy.path.display_name()`.
|
||||
*/
|
||||
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
#if defined(WIN32)
|
||||
void BLI_path_normalize_unc_16(wchar_t *path_16);
|
||||
void BLI_path_normalize_unc(char *path, int path_maxncpy);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Appends a suffix to the `path`, fitting it before the extension
|
||||
*
|
||||
* path = `Foo.png`, suffix = `123`, separator = `_`.
|
||||
* `Foo.png` -> `Foo_123.png`.
|
||||
*
|
||||
* \param path: original (and final) string.
|
||||
* \param path_maxncpy: Maximum length of path.
|
||||
* \param suffix: String to append to the original path.
|
||||
* \param sep: Optional separator character.
|
||||
* \return true if succeeded.
|
||||
*/
|
||||
bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep)
|
||||
ATTR_NONNULL(1, 3, 4);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Comparison / Contains
|
||||
* \{ */
|
||||
|
||||
/* Path string comparisons: case-insensitive for Windows, case-sensitive otherwise. */
|
||||
#if defined(WIN32)
|
||||
|
@ -543,14 +484,172 @@ bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const
|
|||
int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
||||
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/* These values need to be hard-coded in structs, dna does not recognize defines */
|
||||
/* also defined in `DNA_space_types.h`. */
|
||||
/** Return true only if #containee_path is contained in #container_path. */
|
||||
bool BLI_path_contains(const char *container_path, const char *containee_path)
|
||||
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Program Specific Path Functions
|
||||
* \{ */
|
||||
|
||||
#ifdef _WIN32
|
||||
bool BLI_path_program_extensions_add_win32(char *program_name, size_t program_name_maxncpy);
|
||||
#endif
|
||||
/**
|
||||
* Search for a binary (executable)
|
||||
*/
|
||||
bool BLI_path_program_search(char *program_filepath,
|
||||
size_t program_filepath_maxncpy,
|
||||
const char *program_name) ATTR_NONNULL(1, 3);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blender Specific Frame Sequence Encode/Decode
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns in area pointed to by `path` a string of the form `<head><pic><tail>`,
|
||||
* where pic is formatted as `numlen` digits with leading zeroes.
|
||||
*/
|
||||
void BLI_path_sequence_encode(char *path,
|
||||
size_t path_maxncpy,
|
||||
const char *head,
|
||||
const char *tail,
|
||||
unsigned short numlen,
|
||||
int pic);
|
||||
|
||||
/**
|
||||
* Looks for a sequence of decimal digits in `path`, preceding any filename extension,
|
||||
* returning the integer value if found, or 0 if not.
|
||||
*
|
||||
* \param path: String to scan.
|
||||
* \param head: Optional area to return copy of part of `path` prior to digits,
|
||||
* or before dot if no digits.
|
||||
* \param tail: Optional area to return copy of part of `path` following digits,
|
||||
* or from dot if no digits.
|
||||
* \param r_digits_len: Optional to return number of digits found.
|
||||
*/
|
||||
int BLI_path_sequence_decode(const char *path,
|
||||
char *head,
|
||||
size_t head_maxncpy,
|
||||
char *tail,
|
||||
size_t tail_maxncpy,
|
||||
unsigned short *r_digits_len);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blender Specific Frame Number Apply/Strip
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with frame as decimal integer, with leading zeroes as necessary, to make digits.
|
||||
*/
|
||||
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with sta and end as decimal integers, with leading zeroes as necessary, to make digits
|
||||
* digits each, with a hyphen in-between.
|
||||
*/
|
||||
bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits)
|
||||
ATTR_NONNULL(1);
|
||||
/**
|
||||
* Get the frame from a filename formatted by blender's frame scheme
|
||||
*/
|
||||
bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len) ATTR_NONNULL(1, 2, 3);
|
||||
/**
|
||||
* Given a `path` with digits representing frame numbers, replace the digits with the '#'
|
||||
* character and extract the extension.
|
||||
* So: `/some/path_123.jpeg`
|
||||
* Becomes: `/some/path_###` with `r_ext` set to `.jpeg`.
|
||||
*/
|
||||
void BLI_path_frame_strip(char *path, char *r_ext, size_t ext_maxncpy) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range
|
||||
*/
|
||||
bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blender Spesific File Relative Paths
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* These values need to be hard-coded in structs, dna does not recognize defines
|
||||
* (also defined in `DNA_space_types.h`).
|
||||
*
|
||||
* \note In general path functions should *not* depend on these hard coded limits,
|
||||
* there is an exception for:
|
||||
* - #BLI_path_abs
|
||||
* - #BLI_path_rel
|
||||
* These functions deal specifically with `.blend` file paths,
|
||||
* where #FILE_MAX assumed to be the limit of all paths passes into these functions.
|
||||
*
|
||||
* Some parts of the API which use #FILE_MAX which aren't specifically handling blend file paths,
|
||||
* in most cases these can be updated to use #PATH_MAX or a platform specific limit.
|
||||
*/
|
||||
#ifndef FILE_MAXDIR
|
||||
# define FILE_MAXDIR 768
|
||||
# define FILE_MAXFILE 256
|
||||
# define FILE_MAX 1024
|
||||
#endif
|
||||
|
||||
/**
|
||||
* If path begins with "//", strips that and replaces it with `basepath` directory.
|
||||
*
|
||||
* \note Also converts drive-letter prefix to something more sensible
|
||||
* if this is a non-drive-letter-based system.
|
||||
*
|
||||
* \param path: The path to convert.
|
||||
* \param basepath: The directory to base relative paths with.
|
||||
* \return true if the path was relative (started with "//").
|
||||
*/
|
||||
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Replaces `path` with a relative version (prefixed by "//") such that #BLI_path_abs, given
|
||||
* the same `basepath`, will convert it back to its original value.
|
||||
*/
|
||||
void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Does path begin with the special "//" prefix that Blender uses to indicate
|
||||
* a path relative to the .blend file.
|
||||
*/
|
||||
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Current Working Directory Specific Paths
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Checks for a relative path (ignoring Blender's "//") prefix
|
||||
* (unlike `!BLI_path_is_rel(path)`).
|
||||
* When false, #BLI_path_abs_from_cwd would expand the absolute path.
|
||||
*/
|
||||
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Checks for relative path, expanding them relative to the current working directory.
|
||||
* \returns true if the expansion was performed.
|
||||
*
|
||||
* \note Should only be called with command line paths.
|
||||
* This is _not_ something Blender's internal paths support, instead they use the "//" prefix.
|
||||
* In most cases #BLI_path_abs should be used instead.
|
||||
*/
|
||||
bool BLI_path_abs_from_cwd(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Native Slash Defines & Checks
|
||||
* \{ */
|
||||
|
||||
#ifdef WIN32
|
||||
# define SEP '\\'
|
||||
# define ALTSEP '/'
|
||||
|
@ -581,6 +680,42 @@ BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
|
|||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name OS Level Wrappers
|
||||
*
|
||||
* TODO: move these to a different module, they are not path functions.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Sets the specified environment variable to the specified value,
|
||||
* and clears it if `val == NULL`.
|
||||
*/
|
||||
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Only set an environment variable if already not there.
|
||||
* Like Unix `setenv(env, val, 0);`
|
||||
*
|
||||
* (not used anywhere).
|
||||
*/
|
||||
void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Get an environment variable, result has to be used immediately.
|
||||
*
|
||||
* On windows #getenv gets its variables from a static copy of the environment variables taken at
|
||||
* process start-up, causing it to not pick up on environment variables created during runtime.
|
||||
* This function uses an alternative method to get environment variables that does pick up on
|
||||
* runtime environment variables. The result will be UTF-8 encoded.
|
||||
*/
|
||||
const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Current & Parent Directory Defines/Macros
|
||||
* \{ */
|
||||
|
||||
/* Parent and current dir helpers. */
|
||||
#define FILENAME_PARENT ".."
|
||||
#define FILENAME_CURRENT "."
|
||||
|
@ -591,6 +726,8 @@ BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
|
|||
#define FILENAME_IS_CURRPAR(_n) \
|
||||
(((_n)[0] == '.') && (((_n)[1] == '\0') || (((_n)[1] == '.') && ((_n)[2] == '\0'))))
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -191,6 +191,15 @@ bool BLI_str_replace_table_exact(char *string,
|
|||
const char *replace_table[][2],
|
||||
int replace_table_len);
|
||||
|
||||
/**
|
||||
* Write `dst` into the range between `src_beg` & `src_end`,
|
||||
* resize within `string_maxncpy` limits, ensure null terminated.
|
||||
*
|
||||
* \return the length of `string`.
|
||||
*/
|
||||
size_t BLI_str_replace_range(
|
||||
char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst);
|
||||
|
||||
/**
|
||||
* Portable replacement for #snprintf
|
||||
*/
|
||||
|
|
|
@ -42,6 +42,26 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
|
|||
eStrCursorJumpType jump,
|
||||
bool use_init_step);
|
||||
|
||||
/**
|
||||
* Given a position within a string,
|
||||
* return the start and end of the closest sequence of delimited characters.
|
||||
* Typically a word, but can be a sequence of characters (including spaces).
|
||||
*
|
||||
* \note When used for word-selection the caller should set the cursor to `r_end` (by convention).
|
||||
*
|
||||
* \param str: The string with a cursor position
|
||||
* \param str_maxlen: The maximum characters to consider
|
||||
* \param pos: The starting cursor position.
|
||||
* \param r_start: returned start of word/sequence boundary (0-based)
|
||||
* \param r_end: returned end of word/sequence boundary (0-based)
|
||||
*/
|
||||
void BLI_str_cursor_step_bounds_utf8(
|
||||
const char *str, const size_t str_maxlen, int pos, int *r_start, int *r_end);
|
||||
|
||||
/** A UTF32 version of #BLI_str_cursor_step_bounds_utf8 */
|
||||
void BLI_str_cursor_step_bounds_utf32(
|
||||
const char32_t *str, const size_t str_maxlen, int pos, int *r_start, int *r_end);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,14 @@ static int BLI_path_unc_prefix_len(const char *path);
|
|||
static bool BLI_path_is_abs_win32(const char *path);
|
||||
#endif /* WIN32 */
|
||||
|
||||
/**
|
||||
* The maximum number of `#` characters expanded for #BLI_path_frame & #BLI_path_frame_range
|
||||
* Typically 12 is enough and even 16 is very large.
|
||||
* Use a much larger value so hitting the upper limit is not an issue.
|
||||
* Exceeding this limit won't fail either, it will just not insert so many leading zeros.
|
||||
*/
|
||||
#define FILENAME_FRAME_CHARS_MAX FILE_MAX
|
||||
|
||||
int BLI_path_sequence_decode(const char *path,
|
||||
char *head,
|
||||
const size_t head_maxncpy,
|
||||
|
@ -89,7 +97,7 @@ int BLI_path_sequence_decode(const char *path,
|
|||
const long long int ret = strtoll(&(path[nums]), NULL, 10);
|
||||
if (ret >= INT_MIN && ret <= INT_MAX) {
|
||||
if (tail) {
|
||||
strcpy(tail, &path[nume + 1]);
|
||||
BLI_strncpy(tail, &path[nume + 1], tail_maxncpy);
|
||||
}
|
||||
if (head) {
|
||||
BLI_strncpy(head, path, MIN2(head_maxncpy, nums + 1));
|
||||
|
@ -584,7 +592,7 @@ void BLI_path_normalize_unc_16(wchar_t *path_16)
|
|||
}
|
||||
#endif
|
||||
|
||||
void BLI_path_rel(char *path, const char *basename)
|
||||
void BLI_path_rel(char path[FILE_MAX], const char *basepath)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(path, FILE_MAX);
|
||||
|
||||
|
@ -598,24 +606,24 @@ void BLI_path_rel(char *path, const char *basename)
|
|||
}
|
||||
|
||||
/* Also bail out if relative path is not set. */
|
||||
if (basename[0] == '\0') {
|
||||
if (basepath[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (BLI_strnlen(basename, 3) > 2 && !BLI_path_is_abs_win32(basename)) {
|
||||
if (BLI_strnlen(basepath, 3) > 2 && !BLI_path_is_abs_win32(basepath)) {
|
||||
char *ptemp;
|
||||
/* Fix missing volume name in relative base,
|
||||
* can happen with old `recent-files.txt` files. */
|
||||
BLI_windows_get_default_root_dir(temp);
|
||||
ptemp = &temp[2];
|
||||
if (!ELEM(basename[0], '\\', '/')) {
|
||||
if (!ELEM(basepath[0], '\\', '/')) {
|
||||
ptemp++;
|
||||
}
|
||||
BLI_strncpy(ptemp, basename, FILE_MAX - 3);
|
||||
BLI_strncpy(ptemp, basepath, FILE_MAX - 3);
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(temp, basename, FILE_MAX);
|
||||
BLI_strncpy(temp, basepath, FILE_MAX);
|
||||
}
|
||||
|
||||
if (BLI_strnlen(path, 3) > 2) {
|
||||
|
@ -645,7 +653,7 @@ void BLI_path_rel(char *path, const char *basename)
|
|||
}
|
||||
}
|
||||
#else
|
||||
STRNCPY(temp, basename);
|
||||
STRNCPY(temp, basepath);
|
||||
#endif
|
||||
|
||||
BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
|
||||
|
@ -819,9 +827,14 @@ bool BLI_path_parent_dir_until_exists(char *dir)
|
|||
/**
|
||||
* Looks for a sequence of "#" characters in the last slash-separated component of `path`,
|
||||
* returning the indexes of the first and one past the last character in the sequence in
|
||||
* `char_start` and `char_end` respectively. Returns true if such a sequence was found.
|
||||
* `char_start` and `char_end` respectively.
|
||||
*
|
||||
* \param char_start: The first `#` character.
|
||||
* \param char_end: The last `#` character +1.
|
||||
*
|
||||
* \return true if a frame sequence range was found.
|
||||
*/
|
||||
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
|
||||
static bool path_frame_chars_find_range(const char *path, int *char_start, int *char_end)
|
||||
{
|
||||
uint ch_sta, ch_end, i;
|
||||
/* Insert current frame: `file###` -> `file001`. */
|
||||
|
@ -885,18 +898,19 @@ bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits)
|
|||
ensure_digits(path, digits);
|
||||
}
|
||||
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
|
||||
char tmp[FILE_MAX];
|
||||
SNPRINTF(tmp, "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
|
||||
BLI_strncpy(path, tmp, path_maxncpy);
|
||||
if (path_frame_chars_find_range(path, &ch_sta, &ch_end)) {
|
||||
char frame_str[FILENAME_FRAME_CHARS_MAX + 1]; /* One for null. */
|
||||
const int ch_span = MIN2(ch_end - ch_sta, FILENAME_FRAME_CHARS_MAX);
|
||||
BLI_snprintf(frame_str, sizeof(frame_str), "%.*d", ch_span, frame);
|
||||
BLI_str_replace_range(path, path_maxncpy, ch_sta, ch_end, frame_str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BLI_path_frame_range(char *path, int sta, int end, int digits)
|
||||
bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(path, FILE_MAX);
|
||||
BLI_string_debug_size_after_nil(path, path_maxncpy);
|
||||
|
||||
int ch_sta, ch_end;
|
||||
|
||||
|
@ -904,18 +918,11 @@ bool BLI_path_frame_range(char *path, int sta, int end, int digits)
|
|||
ensure_digits(path, digits);
|
||||
}
|
||||
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
|
||||
char tmp[FILE_MAX];
|
||||
SNPRINTF(tmp,
|
||||
"%.*s%.*d-%.*d%s",
|
||||
ch_sta,
|
||||
path,
|
||||
ch_end - ch_sta,
|
||||
sta,
|
||||
ch_end - ch_sta,
|
||||
end,
|
||||
path + ch_end);
|
||||
BLI_strncpy(path, tmp, FILE_MAX);
|
||||
if (path_frame_chars_find_range(path, &ch_sta, &ch_end)) {
|
||||
char frame_str[(FILENAME_FRAME_CHARS_MAX * 2) + 1 + 1]; /* One for null, one for the '-' */
|
||||
const int ch_span = MIN2(ch_end - ch_sta, FILENAME_FRAME_CHARS_MAX);
|
||||
BLI_snprintf(frame_str, sizeof(frame_str), "%.*d-%.*d", ch_span, sta, ch_span, end);
|
||||
BLI_str_replace_range(path, path_maxncpy, ch_sta, ch_end, frame_str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -981,7 +988,7 @@ void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxncpy)
|
|||
bool BLI_path_frame_check_chars(const char *path)
|
||||
{
|
||||
int ch_sta_dummy, ch_end_dummy;
|
||||
return stringframe_chars(path, &ch_sta_dummy, &ch_end_dummy);
|
||||
return path_frame_chars_find_range(path, &ch_sta_dummy, &ch_end_dummy);
|
||||
}
|
||||
|
||||
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
|
||||
|
@ -1023,7 +1030,7 @@ void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, cons
|
|||
}
|
||||
}
|
||||
|
||||
bool BLI_path_abs(char *path, const char *basepath)
|
||||
bool BLI_path_abs(char path[FILE_MAX], const char *basepath)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(path, FILE_MAX);
|
||||
|
||||
|
@ -1146,10 +1153,10 @@ bool BLI_path_abs_from_cwd(char *path, const size_t path_maxncpy)
|
|||
BLI_string_debug_size_after_nil(path, path_maxncpy);
|
||||
|
||||
if (!BLI_path_is_abs_from_cwd(path)) {
|
||||
char cwd[FILE_MAX];
|
||||
char cwd[PATH_MAX];
|
||||
/* In case the full path to the blend isn't used. */
|
||||
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
|
||||
char origpath[FILE_MAX];
|
||||
char origpath[PATH_MAX];
|
||||
STRNCPY(origpath, path);
|
||||
BLI_path_join(path, path_maxncpy, cwd, origpath);
|
||||
}
|
||||
|
@ -1232,7 +1239,7 @@ bool BLI_path_program_search(char *program_filepath,
|
|||
|
||||
path = BLI_getenv("PATH");
|
||||
if (path) {
|
||||
char filepath_test[FILE_MAX];
|
||||
char filepath_test[PATH_MAX];
|
||||
const char *temp;
|
||||
|
||||
do {
|
||||
|
@ -1484,13 +1491,13 @@ bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *file)
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(filepath, filepath_maxncpy);
|
||||
|
||||
char *c = (char *)BLI_path_slash_rfind(filepath);
|
||||
if (!c || ((c - filepath) < filepath_maxncpy - (strlen(file) + 1))) {
|
||||
strcpy(c ? &c[1] : filepath, file);
|
||||
char *c = (char *)BLI_path_basename(filepath);
|
||||
const size_t filename_size = strlen(filename) + 1;
|
||||
if (filename_size <= filepath_maxncpy - (c - filepath)) {
|
||||
memcpy(c, filename, filename_size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1911,11 +1918,17 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
|||
BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
|
||||
|
||||
/* Normalize the paths so we can compare them. */
|
||||
char norm_p1[FILE_MAX];
|
||||
char norm_p2[FILE_MAX];
|
||||
char norm_p1_buf[256];
|
||||
char norm_p2_buf[256];
|
||||
|
||||
STRNCPY(norm_p1, p1);
|
||||
STRNCPY(norm_p2, p2);
|
||||
const size_t p1_size = strlen(p1) + 1;
|
||||
const size_t p2_size = strlen(p2) + 1;
|
||||
|
||||
char *norm_p1 = (p1_size <= sizeof(norm_p1_buf)) ? norm_p1_buf : MEM_mallocN(p1_size, __func__);
|
||||
char *norm_p2 = (p2_size <= sizeof(norm_p2_buf)) ? norm_p2_buf : MEM_mallocN(p2_size, __func__);
|
||||
|
||||
memcpy(norm_p1, p1, p1_size);
|
||||
memcpy(norm_p2, p2, p2_size);
|
||||
|
||||
BLI_path_slash_native(norm_p1);
|
||||
BLI_path_slash_native(norm_p2);
|
||||
|
@ -1923,5 +1936,13 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
|||
BLI_path_normalize(norm_p1);
|
||||
BLI_path_normalize(norm_p2);
|
||||
|
||||
return BLI_path_cmp(norm_p1, norm_p2);
|
||||
const int result = BLI_path_cmp(norm_p1, norm_p2);
|
||||
|
||||
if (norm_p1 != norm_p1_buf) {
|
||||
MEM_freeN(norm_p1);
|
||||
}
|
||||
if (norm_p2 != norm_p2_buf) {
|
||||
MEM_freeN(norm_p2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -552,6 +552,55 @@ bool BLI_str_replace_table_exact(char *string,
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t BLI_str_replace_range(
|
||||
char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst)
|
||||
{
|
||||
int string_len = strlen(string);
|
||||
BLI_assert(src_beg <= src_end);
|
||||
BLI_assert(src_end <= string_len);
|
||||
const int src_len = src_end - src_beg;
|
||||
int dst_len = strlen(dst);
|
||||
|
||||
if (src_len < dst_len) {
|
||||
/* Grow, first handle special cases. */
|
||||
|
||||
/* Special case, the src_end is entirely clipped. */
|
||||
if (UNLIKELY(string_maxncpy <= src_beg + dst_len)) {
|
||||
/* There is only room for the destination. */
|
||||
dst_len = ((int)string_maxncpy - src_beg) - 1;
|
||||
string_len = src_end;
|
||||
string[string_len] = '\0';
|
||||
}
|
||||
|
||||
const int ofs = dst_len - src_len;
|
||||
/* Clip the string when inserting the destination string exceeds `string_maxncpy`. */
|
||||
if (string_len + ofs >= string_maxncpy) {
|
||||
string_len = ((int)string_maxncpy - ofs) - 1;
|
||||
string[string_len] = '\0';
|
||||
BLI_assert(src_end <= string_len);
|
||||
}
|
||||
|
||||
/* Grow. */
|
||||
memmove(string + (src_end + ofs), string + src_end, (size_t)(string_len - src_end) + 1);
|
||||
string_len += ofs;
|
||||
}
|
||||
else if (src_len > dst_len) {
|
||||
/* Shrink. */
|
||||
const int ofs = src_len - dst_len;
|
||||
memmove(string + (src_end - ofs), string + src_end, (size_t)(string_len - src_end) + 1);
|
||||
string_len -= ofs;
|
||||
}
|
||||
else { /* Simple case, no resizing. */
|
||||
BLI_assert(src_len == dst_len);
|
||||
}
|
||||
|
||||
if (dst_len > 0) {
|
||||
memcpy(string + src_beg, dst, (size_t)dst_len);
|
||||
}
|
||||
BLI_assert(string[string_len] == '\0');
|
||||
return (size_t)string_len;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
# pragma GCC diagnostic error "-Wsign-conversion"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The category of character as returned by #cursor_delim_type_unicode.
|
||||
*
|
||||
* \note Don't compare with any values besides #STRCUR_DELIM_NONE as cursor motion
|
||||
* should only delimit on changes, not treat some groups differently.
|
||||
*
|
||||
* For range calculation the order prioritizes expansion direction,
|
||||
* when the cursor is between two different categories, "hug" the smaller values.
|
||||
* Where white-space gets lowest priority. See #BLI_str_cursor_step_bounds_utf8.
|
||||
* This is done so expanding the range at a word boundary always chooses the word instead
|
||||
* of the white-space before or after it.
|
||||
*/
|
||||
typedef enum eStrCursorDelimType {
|
||||
STRCUR_DELIM_NONE,
|
||||
STRCUR_DELIM_ALPHANUMERIC,
|
||||
|
@ -24,8 +36,8 @@ typedef enum eStrCursorDelimType {
|
|||
STRCUR_DELIM_BRACE,
|
||||
STRCUR_DELIM_OPERATOR,
|
||||
STRCUR_DELIM_QUOTE,
|
||||
STRCUR_DELIM_WHITESPACE,
|
||||
STRCUR_DELIM_OTHER,
|
||||
STRCUR_DELIM_WHITESPACE,
|
||||
} eStrCursorDelimType;
|
||||
|
||||
static eStrCursorDelimType cursor_delim_type_unicode(const uint uch)
|
||||
|
@ -314,3 +326,46 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
|
|||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_str_cursor_step_bounds_utf8(
|
||||
const char *str, const size_t str_maxlen, const int pos, int *r_start, int *r_end)
|
||||
{
|
||||
/* Identify the type of characters are on either side of the current cursor position. */
|
||||
const eStrCursorDelimType prev = (pos > 0) ? cursor_delim_type_utf8(str, str_maxlen, pos - 1) :
|
||||
STRCUR_DELIM_NONE;
|
||||
const eStrCursorDelimType next = (pos < str_maxlen) ?
|
||||
cursor_delim_type_utf8(str, str_maxlen, pos) :
|
||||
STRCUR_DELIM_NONE;
|
||||
*r_start = pos;
|
||||
*r_end = pos;
|
||||
|
||||
if ((prev <= next) && (prev != STRCUR_DELIM_NONE)) {
|
||||
/* Expand backward if we are between similar content. */
|
||||
BLI_str_cursor_step_utf8(str, str_maxlen, r_start, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
if ((prev >= next) && (next != STRCUR_DELIM_NONE)) {
|
||||
/* Expand forward if we are between similar content, after whitespace, or at beginning. */
|
||||
BLI_str_cursor_step_utf8(str, str_maxlen, r_end, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_str_cursor_step_bounds_utf32(
|
||||
const char32_t *str, const size_t str_maxlen, const int pos, int *r_start, int *r_end)
|
||||
{
|
||||
/* Identify the type of characters are on either side of the current cursor position. */
|
||||
const eStrCursorDelimType prev = (pos > 0) ? cursor_delim_type_unicode(str[pos - 1]) :
|
||||
STRCUR_DELIM_NONE;
|
||||
const eStrCursorDelimType next = (pos < str_maxlen) ? cursor_delim_type_unicode(str[pos]) :
|
||||
STRCUR_DELIM_NONE;
|
||||
*r_start = pos;
|
||||
*r_end = pos;
|
||||
|
||||
if ((prev <= next) && (prev != STRCUR_DELIM_NONE)) {
|
||||
/* Expand backward if we are between similar content, before whitespace, or at end. */
|
||||
BLI_str_cursor_step_utf32(str, str_maxlen, r_start, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
if ((prev >= next) && (next != STRCUR_DELIM_NONE)) {
|
||||
/* Expand forward if we are between similar content, after whitespace, or at beginning. */
|
||||
BLI_str_cursor_step_utf32(str, str_maxlen, r_end, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -638,6 +638,15 @@ TEST(path_util, Frame)
|
|||
EXPECT_TRUE(ret);
|
||||
EXPECT_STREQ(path, "test_-0100");
|
||||
}
|
||||
|
||||
/* Ensure very large ranges work. */
|
||||
{
|
||||
char path[FILE_MAX * 2];
|
||||
memset(path, '#', sizeof(path));
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
ret = BLI_path_frame(path, sizeof(path), 123456789, 0);
|
||||
EXPECT_TRUE(BLI_str_endswith(path, "0123456789"));
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -987,7 +996,7 @@ TEST(path_util, FrameCheckChars)
|
|||
char path[FILE_MAX]; \
|
||||
bool ret; \
|
||||
STRNCPY(path, input_path); \
|
||||
ret = BLI_path_frame_range(path, sta, end, digits); \
|
||||
ret = BLI_path_frame_range(path, sizeof(path), sta, end, digits); \
|
||||
if (expect_outpath == nullptr) { \
|
||||
EXPECT_FALSE(ret); \
|
||||
} \
|
||||
|
|
|
@ -122,6 +122,47 @@ TEST(string, StrCopyUTF8_TerminateEncodingEarly)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name String Replace
|
||||
* \{ */
|
||||
|
||||
TEST(string, StrReplaceRange)
|
||||
{
|
||||
#define STR_REPLACE_RANGE(src, size, beg, end, dst, result_expect) \
|
||||
{ \
|
||||
char string[size] = src; \
|
||||
BLI_str_replace_range(string, sizeof(string), beg, end, dst); \
|
||||
EXPECT_STREQ(string, result_expect); \
|
||||
}
|
||||
|
||||
STR_REPLACE_RANGE("a ", 5, 2, 2, "b!", "a b!");
|
||||
STR_REPLACE_RANGE("a ", 4, 2, 2, "b!", "a b");
|
||||
STR_REPLACE_RANGE("a ", 5, 1, 2, "b!", "ab!");
|
||||
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "A", "XAYZ");
|
||||
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "AB", "XABY");
|
||||
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "ABC", "XABC");
|
||||
|
||||
/* Add at the end when there is no room (no-op). */
|
||||
STR_REPLACE_RANGE("XYZA", 5, 4, 4, "?", "XYZA");
|
||||
/* Add at the start, replace all contents. */
|
||||
STR_REPLACE_RANGE("XYZ", 4, 0, 0, "ABC", "ABC");
|
||||
STR_REPLACE_RANGE("XYZ", 7, 0, 0, "ABC", "ABCXYZ");
|
||||
/* Only remove. */
|
||||
STR_REPLACE_RANGE("XYZ", 4, 1, 3, "", "X");
|
||||
STR_REPLACE_RANGE("XYZ", 4, 0, 2, "", "Z");
|
||||
STR_REPLACE_RANGE("XYZ", 4, 0, 3, "", "");
|
||||
/* Only Add. */
|
||||
STR_REPLACE_RANGE("", 4, 0, 0, "XYZ", "XYZ");
|
||||
STR_REPLACE_RANGE("", 4, 0, 0, "XYZ?", "XYZ");
|
||||
/* Do nothing. */
|
||||
STR_REPLACE_RANGE("", 1, 0, 0, "?", "");
|
||||
STR_REPLACE_RANGE("", 1, 0, 0, "", "");
|
||||
|
||||
#undef STR_REPLACE_RANGE
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name String Partition
|
||||
* \{ */
|
||||
|
|
|
@ -239,7 +239,8 @@ static const char *library_parent_filepath(Library *lib)
|
|||
|
||||
struct NewAddress {
|
||||
void *newp;
|
||||
/* `nr` is "user count" for data, and ID code for libdata. */
|
||||
|
||||
/** `nr` is "user count" for data, and ID code for libdata. */
|
||||
int nr;
|
||||
};
|
||||
|
||||
|
@ -261,9 +262,9 @@ static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr,
|
|||
onm->map.add_overwrite(oldaddr, NewAddress{newaddr, nr});
|
||||
}
|
||||
|
||||
static void oldnewmap_lib_insert(FileData *fd, const void *oldaddr, ID *newaddr, int nr)
|
||||
static void oldnewmap_lib_insert(FileData *fd, const void *oldaddr, ID *newaddr, int id_code)
|
||||
{
|
||||
oldnewmap_insert(fd->libmap, oldaddr, newaddr, nr);
|
||||
oldnewmap_insert(fd->libmap, oldaddr, newaddr, id_code);
|
||||
}
|
||||
|
||||
void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr)
|
||||
|
@ -1297,8 +1298,8 @@ void blo_filedata_free(FileData *fd)
|
|||
if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP)) {
|
||||
oldnewmap_free(fd->libmap);
|
||||
}
|
||||
if (fd->old_idmap != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap);
|
||||
if (fd->old_idmap_uuid != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap_uuid);
|
||||
}
|
||||
blo_cache_storage_end(fd);
|
||||
if (fd->bheadmap) {
|
||||
|
@ -1533,10 +1534,10 @@ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
|
|||
|
||||
void blo_make_old_idmap_from_main(FileData *fd, Main *bmain)
|
||||
{
|
||||
if (fd->old_idmap != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap);
|
||||
if (fd->old_idmap_uuid != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap_uuid);
|
||||
}
|
||||
fd->old_idmap = BKE_main_idmap_create(bmain, false, nullptr, MAIN_IDMAP_TYPE_UUID);
|
||||
fd->old_idmap_uuid = BKE_main_idmap_create(bmain, false, nullptr, MAIN_IDMAP_TYPE_UUID);
|
||||
}
|
||||
|
||||
struct BLOCacheStorage {
|
||||
|
@ -1898,7 +1899,7 @@ static void direct_link_id_override_property_cb(BlendDataReader *reader, void *d
|
|||
}
|
||||
|
||||
static void direct_link_id_common(
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag);
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int id_tag);
|
||||
|
||||
static void direct_link_id_embedded_id(BlendDataReader *reader,
|
||||
Library *current_library,
|
||||
|
@ -1990,7 +1991,7 @@ static int direct_link_id_restore_recalc(const FileData *fd,
|
|||
}
|
||||
|
||||
static void direct_link_id_common(
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag)
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int id_tag)
|
||||
{
|
||||
if (!BLO_read_data_is_undo(reader)) {
|
||||
/* When actually reading a file, we do want to reset/re-generate session UUIDS.
|
||||
|
@ -1998,7 +1999,7 @@ static void direct_link_id_common(
|
|||
id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
|
||||
}
|
||||
|
||||
if ((tag & LIB_TAG_TEMP_MAIN) == 0) {
|
||||
if ((id_tag & LIB_TAG_TEMP_MAIN) == 0) {
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
}
|
||||
|
||||
|
@ -2011,10 +2012,10 @@ static void direct_link_id_common(
|
|||
|
||||
/* Initialize with provided tag. */
|
||||
if (BLO_read_data_is_undo(reader)) {
|
||||
id->tag = tag | (id->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
id->tag = (id_tag & ~LIB_TAG_KEEP_ON_UNDO) | (id->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
}
|
||||
else {
|
||||
id->tag = tag;
|
||||
id->tag = id_tag;
|
||||
}
|
||||
|
||||
if (ID_IS_LINKED(id)) {
|
||||
|
@ -2024,7 +2025,7 @@ static void direct_link_id_common(
|
|||
BLO_read_data_address(reader, &id->library_weak_reference);
|
||||
}
|
||||
|
||||
if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
|
||||
if (id_tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
|
||||
/* For placeholder we only need to set the tag and properly initialize generic ID fields above,
|
||||
* no further data to read. */
|
||||
return;
|
||||
|
@ -3071,13 +3072,13 @@ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID
|
|||
|
||||
/* For undo, restore unchanged datablock from old main. */
|
||||
static void read_libblock_undo_restore_identical(
|
||||
FileData *fd, Main *main, const ID * /*id*/, ID *id_old, const int tag)
|
||||
FileData *fd, Main *main, const ID * /*id*/, ID *id_old, const int id_tag)
|
||||
{
|
||||
BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
|
||||
BLI_assert(id_old != nullptr);
|
||||
|
||||
/* Some tags need to be preserved here. */
|
||||
id_old->tag = tag | (id_old->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
id_old->tag = (id_tag & ~LIB_TAG_KEEP_ON_UNDO) | (id_old->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
id_old->lib = main->curlib;
|
||||
id_old->us = ID_FAKE_USERS(id_old);
|
||||
/* Do not reset id->icon_id here, memory allocated for it remains valid. */
|
||||
|
@ -3146,7 +3147,7 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
|
|||
}
|
||||
|
||||
static bool read_libblock_undo_restore(
|
||||
FileData *fd, Main *main, BHead *bhead, const int tag, ID **r_id_old)
|
||||
FileData *fd, Main *main, BHead *bhead, int id_tag, ID **r_id_old)
|
||||
{
|
||||
/* Get pointer to memory of new ID that we will be reading. */
|
||||
const ID *id = static_cast<const ID *>(peek_struct_undo(fd, bhead));
|
||||
|
@ -3181,8 +3182,8 @@ static bool read_libblock_undo_restore(
|
|||
|
||||
/* Find the 'current' existing ID we want to reuse instead of the one we
|
||||
* would read from the undo memfile. */
|
||||
BLI_assert(fd->old_idmap != nullptr);
|
||||
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
|
||||
BLI_assert(fd->old_idmap_uuid != nullptr);
|
||||
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap_uuid, id->session_uuid);
|
||||
}
|
||||
|
||||
if (id_old != nullptr && read_libblock_is_identical(fd, bhead)) {
|
||||
|
@ -3198,7 +3199,7 @@ static bool read_libblock_undo_restore(
|
|||
* it will tell us which ID is re-used from old Main, and which one is actually new. */
|
||||
/* Also do not add LIB_TAG_NEED_LINK, those IDs will never be re-liblinked, hence that tag will
|
||||
* never be cleared, leading to critical issue in link/append code. */
|
||||
const int id_tag = tag | LIB_TAG_UNDO_OLD_ID_REUSED;
|
||||
id_tag |= LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED;
|
||||
read_libblock_undo_restore_identical(fd, main, id, id_old, id_tag);
|
||||
|
||||
/* Insert into library map for lookup by newly read datablocks (with pointer value bhead->old).
|
||||
|
@ -3236,7 +3237,7 @@ static bool read_libblock_undo_restore(
|
|||
static BHead *read_libblock(FileData *fd,
|
||||
Main *main,
|
||||
BHead *bhead,
|
||||
const int tag,
|
||||
int id_tag,
|
||||
const bool placeholder_set_indirect_extern,
|
||||
ID **r_id)
|
||||
{
|
||||
|
@ -3257,7 +3258,7 @@ static BHead *read_libblock(FileData *fd,
|
|||
* So undo case does not seem to be affected by this. A future cleanup should try to remove
|
||||
* most of this related code in the future, and instead assert that both `r_id` and
|
||||
* `main->id_map` are `nullptr`. */
|
||||
if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) {
|
||||
if (read_libblock_undo_restore(fd, main, bhead, id_tag, &id_old)) {
|
||||
if (r_id) {
|
||||
*r_id = id_old;
|
||||
}
|
||||
|
@ -3307,7 +3308,7 @@ static BHead *read_libblock(FileData *fd,
|
|||
|
||||
/* Set tag for new datablock to indicate lib linking and versioning needs
|
||||
* to be done still. */
|
||||
int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
|
||||
id_tag |= (LIB_TAG_NEED_LINK | LIB_TAG_NEW);
|
||||
|
||||
if (bhead->code == ID_LINK_PLACEHOLDER) {
|
||||
/* Read placeholder for linked datablock. */
|
||||
|
@ -3598,7 +3599,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
|
|||
}
|
||||
|
||||
if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo &&
|
||||
(id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0)
|
||||
(id->tag & LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED) != 0)
|
||||
{
|
||||
/* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across
|
||||
* current undo step, and old IDs re-use their old memory address, we do not need to liblink
|
||||
|
@ -4567,7 +4568,7 @@ static void library_link_end(Main *mainl, FileData **fd, const int flag)
|
|||
|
||||
/* Some versioning code does expect some proper userrefcounting, e.g. in conversion from
|
||||
* groups to collections... We could optimize out that first call when we are reading a
|
||||
* current version file, but again this is really not a bottle neck currently. so not worth
|
||||
* current version file, but again this is really not a bottle neck currently. So not worth
|
||||
* it. */
|
||||
BKE_main_id_refcount_recompute(mainvar, false);
|
||||
|
||||
|
|
|
@ -94,7 +94,14 @@ typedef struct FileData {
|
|||
|
||||
struct OldNewMap *datamap;
|
||||
struct OldNewMap *globmap;
|
||||
|
||||
/**
|
||||
* Store mapping from old ID pointers (the values they have in the .blend file) to new ones,
|
||||
* typically from value in `bhead->old` to address in memory where the ID was read.
|
||||
* Used during liblinking process (see #lib_link_all).
|
||||
*/
|
||||
struct OldNewMap *libmap;
|
||||
|
||||
struct OldNewMap *packedmap;
|
||||
struct BLOCacheStorage *cache_storage;
|
||||
|
||||
|
@ -107,7 +114,10 @@ typedef struct FileData {
|
|||
ListBase *mainlist;
|
||||
/** Used for undo. */
|
||||
ListBase *old_mainlist;
|
||||
struct IDNameLib_Map *old_idmap;
|
||||
/**
|
||||
* IDMap using uuids as keys of all the old IDs in the old bmain. Used during undo to find a
|
||||
* matching old data when reading a new ID. */
|
||||
struct IDNameLib_Map *old_idmap_uuid;
|
||||
|
||||
struct BlendFileReadReport *reports;
|
||||
} FileData;
|
||||
|
@ -119,7 +129,7 @@ struct Main;
|
|||
void blo_join_main(ListBase *mainlist);
|
||||
void blo_split_main(ListBase *mainlist, struct Main *main);
|
||||
|
||||
BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath);
|
||||
struct BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath);
|
||||
|
||||
/**
|
||||
* On each new library added, it now checks for the current #FileData and expands relativeness
|
||||
|
|
|
@ -4374,6 +4374,20 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable the iTaSC ITASC_TRANSLATE_ROOT_BONES flag for backward compatibility.
|
||||
* See #104606. */
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
if (ob->type != OB_ARMATURE || ob->pose == nullptr) {
|
||||
continue;
|
||||
}
|
||||
bPose *pose = ob->pose;
|
||||
if (pose->iksolver != IKSOLVER_ITASC || pose->ikparam == nullptr) {
|
||||
continue;
|
||||
}
|
||||
bItasc *ikparam = (bItasc *)pose->ikparam;
|
||||
ikparam->flag |= ITASC_TRANSLATE_ROOT_BONES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,6 +73,7 @@ set(SRC
|
|||
cached_resources/intern/cached_mask.cc
|
||||
cached_resources/intern/cached_texture.cc
|
||||
cached_resources/intern/morphological_distance_feather_weights.cc
|
||||
cached_resources/intern/ocio_color_space_conversion_shader.cc
|
||||
cached_resources/intern/smaa_precomputed_textures.cc
|
||||
cached_resources/intern/symmetric_blur_weights.cc
|
||||
cached_resources/intern/symmetric_separable_blur_weights.cc
|
||||
|
@ -81,6 +82,7 @@ set(SRC
|
|||
cached_resources/COM_cached_resource.hh
|
||||
cached_resources/COM_cached_texture.hh
|
||||
cached_resources/COM_morphological_distance_feather_weights.hh
|
||||
cached_resources/COM_ocio_color_space_conversion_shader.hh
|
||||
cached_resources/COM_smaa_precomputed_textures.hh
|
||||
cached_resources/COM_symmetric_blur_weights.hh
|
||||
cached_resources/COM_symmetric_separable_blur_weights.hh
|
||||
|
@ -173,6 +175,7 @@ set(GLSL_SRC
|
|||
shaders/library/gpu_shader_compositor_main.glsl
|
||||
shaders/library/gpu_shader_compositor_map_value.glsl
|
||||
shaders/library/gpu_shader_compositor_normal.glsl
|
||||
shaders/library/gpu_shader_compositor_ocio_processor.glsl
|
||||
shaders/library/gpu_shader_compositor_posterize.glsl
|
||||
shaders/library/gpu_shader_compositor_separate_combine.glsl
|
||||
shaders/library/gpu_shader_compositor_set_alpha.glsl
|
||||
|
@ -268,4 +271,18 @@ if(WITH_TBB)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_OPENCOLORIO)
|
||||
add_definitions(
|
||||
-DWITH_OCIO
|
||||
)
|
||||
|
||||
list(APPEND INC_SYS
|
||||
${OPENCOLORIO_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
${OPENCOLORIO_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "COM_cached_mask.hh"
|
||||
#include "COM_cached_texture.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_ocio_color_space_conversion_shader.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
#include "COM_symmetric_separable_blur_weights.hh"
|
||||
|
@ -41,6 +42,7 @@ class StaticCacheManager {
|
|||
CachedTextureContainer cached_textures;
|
||||
CachedMaskContainer cached_masks;
|
||||
SMAAPrecomputedTexturesContainer smaa_precomputed_textures;
|
||||
OCIOColorSpaceConversionShaderContainer ocio_color_space_conversion_shaders;
|
||||
|
||||
/* Reset the cache manager by deleting the cached resources that are no longer needed because
|
||||
* they weren't used in the last evaluation and prepare the remaining cached resources to track
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "COM_cached_resource.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Key.
|
||||
*/
|
||||
class OCIOColorSpaceConversionShaderKey {
|
||||
public:
|
||||
std::string source;
|
||||
std::string target;
|
||||
std::string config_cache_id;
|
||||
|
||||
OCIOColorSpaceConversionShaderKey(std::string source,
|
||||
std::string target,
|
||||
std::string config_cache_id);
|
||||
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
bool operator==(const OCIOColorSpaceConversionShaderKey &a,
|
||||
const OCIOColorSpaceConversionShaderKey &b);
|
||||
|
||||
class GPUShaderCreator;
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader.
|
||||
*
|
||||
* A cached resource that creates and caches a GPU shader that converts the source OCIO color space
|
||||
* of an image into a different target OCIO color space. */
|
||||
class OCIOColorSpaceConversionShader : public CachedResource {
|
||||
private:
|
||||
std::shared_ptr<GPUShaderCreator> shader_creator_;
|
||||
|
||||
public:
|
||||
OCIOColorSpaceConversionShader(std::string source, std::string target);
|
||||
|
||||
GPUShader *bind_shader_and_resources();
|
||||
|
||||
void unbind_shader_and_resources();
|
||||
|
||||
const char *input_sampler_name();
|
||||
|
||||
const char *output_image_name();
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Container.
|
||||
*/
|
||||
class OCIOColorSpaceConversionShaderContainer : CachedResourceContainer {
|
||||
private:
|
||||
Map<OCIOColorSpaceConversionShaderKey, std::unique_ptr<OCIOColorSpaceConversionShader>> map_;
|
||||
|
||||
public:
|
||||
void reset() override;
|
||||
|
||||
/* Check if there is an available OCIOColorSpaceConversionShader cached resource with the given
|
||||
* parameters in the container, if one exists, return it, otherwise, return a newly created one
|
||||
* and add it to the container. In both cases, tag the cached resource as needed to keep it
|
||||
* cached for the next evaluation. */
|
||||
OCIOColorSpaceConversionShader &get(std::string source, std::string target);
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,502 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
#include "GPU_uniform_buffer.h"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "COM_ocio_color_space_conversion_shader.hh"
|
||||
|
||||
#if defined(WITH_OCIO)
|
||||
# include <OpenColorIO/OpenColorIO.h>
|
||||
#endif
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Key.
|
||||
*/
|
||||
|
||||
OCIOColorSpaceConversionShaderKey::OCIOColorSpaceConversionShaderKey(std::string source,
|
||||
std::string target,
|
||||
std::string config_cache_id)
|
||||
: source(source), target(target), config_cache_id(config_cache_id)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t OCIOColorSpaceConversionShaderKey::hash() const
|
||||
{
|
||||
return get_default_hash_3(source, target, config_cache_id);
|
||||
}
|
||||
|
||||
bool operator==(const OCIOColorSpaceConversionShaderKey &a,
|
||||
const OCIOColorSpaceConversionShaderKey &b)
|
||||
{
|
||||
return a.source == b.source && a.target == b.target && a.config_cache_id == b.config_cache_id;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GPU Shader Creator.
|
||||
*/
|
||||
|
||||
#if defined(WITH_OCIO)
|
||||
|
||||
namespace OCIO = OCIO_NAMESPACE;
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
/* A subclass of OCIO::GpuShaderCreator that constructs the shader using a ShaderCreateInfo. The
|
||||
* Create method should be used to construct the creator, then the extractGpuShaderInfo() method of
|
||||
* the appropriate OCIO::GPUProcessor should be called passing in the creator. After construction,
|
||||
* the constructed compute shader can be used by calling the bind_shader_and_resources() method,
|
||||
* followed by binding the input texture and output image using their names input_sampler_name()
|
||||
* and output_image_name(), following by dispatching the shader on the domain of the input, and
|
||||
* finally calling the unbind_shader_and_resources() method.
|
||||
*
|
||||
* Upon calling the extractGpuShaderInfo(), all the transforms in the GPU processor will add their
|
||||
* needed resources by calling the respective addUniform() and add[3D]Texture() methods. Then, the
|
||||
* shader code of all transforms will be generated and passed to the createShaderText() method,
|
||||
* generating the full code of the processor. Finally, the finalize() method will be called to
|
||||
* finally create the shader. */
|
||||
class GPUShaderCreator : public OCIO::GpuShaderCreator {
|
||||
public:
|
||||
static std::shared_ptr<GPUShaderCreator> Create()
|
||||
{
|
||||
std::shared_ptr<GPUShaderCreator> instance = std::make_shared<GPUShaderCreator>();
|
||||
instance->setLanguage(OCIO::GPU_LANGUAGE_GLSL_4_0);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/* Not used, but needs to be overridden, so return a nullptr. */
|
||||
OCIO::GpuShaderCreatorRcPtr clone() const override
|
||||
{
|
||||
return OCIO::GpuShaderCreatorRcPtr();
|
||||
}
|
||||
|
||||
/* This is ignored since we query using our own GPU capabilities system. */
|
||||
void setTextureMaxWidth(unsigned max_width) override {}
|
||||
|
||||
unsigned getTextureMaxWidth() const noexcept override
|
||||
{
|
||||
return GPU_max_texture_size();
|
||||
}
|
||||
|
||||
bool addUniform(const char *name, const DoubleGetter &get_double) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.push_constant(Type::FLOAT, resource_name);
|
||||
|
||||
float_uniforms_.add(resource_name, get_double);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name, const BoolGetter &get_bool) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.push_constant(Type::BOOL, resource_name);
|
||||
|
||||
boolean_uniforms_.add(name, get_bool);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name, const Float3Getter &get_float3) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.push_constant(Type::VEC3, resource_name);
|
||||
|
||||
vector_uniforms_.add(resource_name, get_float3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name,
|
||||
const SizeGetter &get_size,
|
||||
const VectorFloatGetter &get_vector_float) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.uniform_buf(buffers_sizes_.size(), "float", resource_name);
|
||||
|
||||
float_buffers_.add(resource_name, get_vector_float);
|
||||
buffers_sizes_.add(resource_name, get_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name,
|
||||
const SizeGetter &get_size,
|
||||
const VectorIntGetter &get_vector_int) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.uniform_buf(buffers_sizes_.size(), "int", resource_name);
|
||||
|
||||
int_buffers_.add(name, get_vector_int);
|
||||
buffers_sizes_.add(name, get_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void addTexture(const char *texture_name,
|
||||
const char *sampler_name,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
TextureType channel,
|
||||
OCIO::Interpolation interpolation,
|
||||
const float *values) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(sampler_name))) {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
|
||||
GPUTexture *texture;
|
||||
eGPUTextureFormat texture_format = (channel == TEXTURE_RGB_CHANNEL) ? GPU_RGB16F : GPU_R16F;
|
||||
/* A height of 1 indicates a 1D texture according to the OCIO API. */
|
||||
if (height == 1) {
|
||||
texture = GPU_texture_create_1d(
|
||||
texture_name, width, 1, texture_format, GPU_TEXTURE_USAGE_SHADER_READ, values);
|
||||
shader_create_info_.sampler(textures_.size() + 1, ImageType::FLOAT_1D, resource_name);
|
||||
}
|
||||
else {
|
||||
texture = GPU_texture_create_2d(
|
||||
texture_name, width, height, 1, texture_format, GPU_TEXTURE_USAGE_SHADER_READ, values);
|
||||
shader_create_info_.sampler(textures_.size() + 1, ImageType::FLOAT_2D, resource_name);
|
||||
}
|
||||
GPU_texture_filter_mode(texture, interpolation != OCIO::INTERP_NEAREST);
|
||||
|
||||
textures_.add(sampler_name, texture);
|
||||
}
|
||||
|
||||
void add3DTexture(const char *texture_name,
|
||||
const char *sampler_name,
|
||||
unsigned size,
|
||||
OCIO::Interpolation interpolation,
|
||||
const float *values) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(sampler_name))) {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.sampler(textures_.size() + 1, ImageType::FLOAT_3D, resource_name);
|
||||
|
||||
GPUTexture *texture = GPU_texture_create_3d(
|
||||
texture_name, size, size, size, 1, GPU_RGB16F, GPU_TEXTURE_USAGE_SHADER_READ, values);
|
||||
GPU_texture_filter_mode(texture, interpolation != OCIO::INTERP_NEAREST);
|
||||
|
||||
textures_.add(sampler_name, texture);
|
||||
}
|
||||
|
||||
/* This gets called before the finalize() method to construct the shader code. We just
|
||||
* concatenate the code except for the declarations section. That's because the ShaderCreateInfo
|
||||
* will add the declaration itself. */
|
||||
void createShaderText(const char *declarations,
|
||||
const char *helper_methods,
|
||||
const char *function_header,
|
||||
const char *function_body,
|
||||
const char *function_footer) override
|
||||
{
|
||||
shader_code_ += helper_methods;
|
||||
shader_code_ += function_header;
|
||||
shader_code_ += function_body;
|
||||
shader_code_ += function_footer;
|
||||
}
|
||||
|
||||
/* This gets called when all resources were added using the respective addUniform() or
|
||||
* add[3D]Texture() methods and the shader code was generated using the createShaderText()
|
||||
* method. That is, we are ready to complete the ShaderCreateInfo and create a shader from it. */
|
||||
void finalize() override
|
||||
{
|
||||
GpuShaderCreator::finalize();
|
||||
|
||||
shader_create_info_.local_group_size(16, 16);
|
||||
shader_create_info_.sampler(0, ImageType::FLOAT_2D, input_sampler_name());
|
||||
shader_create_info_.image(
|
||||
0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, output_image_name());
|
||||
shader_create_info_.compute_source("gpu_shader_compositor_ocio_processor.glsl");
|
||||
shader_create_info_.compute_source_generated += shader_code_;
|
||||
|
||||
GPUShaderCreateInfo *info = reinterpret_cast<GPUShaderCreateInfo *>(&shader_create_info_);
|
||||
shader_ = GPU_shader_create_from_info(info);
|
||||
}
|
||||
|
||||
GPUShader *bind_shader_and_resources()
|
||||
{
|
||||
GPU_shader_bind(shader_);
|
||||
|
||||
for (auto item : float_uniforms_.items()) {
|
||||
GPU_shader_uniform_1f(shader_, item.key.c_str(), item.value());
|
||||
}
|
||||
|
||||
for (auto item : boolean_uniforms_.items()) {
|
||||
GPU_shader_uniform_1b(shader_, item.key.c_str(), item.value());
|
||||
}
|
||||
|
||||
for (auto item : vector_uniforms_.items()) {
|
||||
GPU_shader_uniform_3fv(shader_, item.key.c_str(), item.value().data());
|
||||
}
|
||||
|
||||
for (auto item : float_buffers_.items()) {
|
||||
GPUUniformBuf *buffer = GPU_uniformbuf_create_ex(
|
||||
buffers_sizes_.lookup(item.key)(), item.value(), item.key.c_str());
|
||||
const int ubo_location = GPU_shader_get_ubo_binding(shader_, item.key.c_str());
|
||||
GPU_uniformbuf_bind(buffer, ubo_location);
|
||||
uniform_buffers_.append(buffer);
|
||||
}
|
||||
|
||||
for (auto item : int_buffers_.items()) {
|
||||
GPUUniformBuf *buffer = GPU_uniformbuf_create_ex(
|
||||
buffers_sizes_.lookup(item.key)(), item.value(), item.key.c_str());
|
||||
const int ubo_location = GPU_shader_get_ubo_binding(shader_, item.key.c_str());
|
||||
GPU_uniformbuf_bind(buffer, ubo_location);
|
||||
uniform_buffers_.append(buffer);
|
||||
}
|
||||
|
||||
for (auto item : textures_.items()) {
|
||||
const int texture_image_unit = GPU_shader_get_sampler_binding(shader_, item.key.c_str());
|
||||
GPU_texture_bind(item.value, texture_image_unit);
|
||||
}
|
||||
|
||||
return shader_;
|
||||
}
|
||||
|
||||
void unbind_shader_and_resources()
|
||||
{
|
||||
for (GPUUniformBuf *buffer : uniform_buffers_) {
|
||||
GPU_uniformbuf_unbind(buffer);
|
||||
GPU_uniformbuf_free(buffer);
|
||||
}
|
||||
|
||||
for (GPUTexture *texture : textures_.values()) {
|
||||
GPU_texture_unbind(texture);
|
||||
}
|
||||
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
const char *input_sampler_name()
|
||||
{
|
||||
return "input_tx";
|
||||
}
|
||||
|
||||
const char *output_image_name()
|
||||
{
|
||||
return "output_img";
|
||||
}
|
||||
|
||||
~GPUShaderCreator() override
|
||||
{
|
||||
for (GPUTexture *texture : textures_.values()) {
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
|
||||
GPU_shader_free(shader_);
|
||||
}
|
||||
|
||||
private:
|
||||
/* The processor shader and the ShaderCreateInfo used to construct it. Constructed and
|
||||
* initialized in the finalize() method. */
|
||||
GPUShader *shader_ = nullptr;
|
||||
ShaderCreateInfo shader_create_info_ = ShaderCreateInfo("OCIO Processor");
|
||||
|
||||
/* Stores the generated OCIOMain function as well as a number of helper functions. Initialized in
|
||||
* the createShaderText() method. */
|
||||
std::string shader_code_;
|
||||
|
||||
/* Maps that associates the name of a uniform with a getter function that returns its value.
|
||||
* Initialized in the respective addUniform() methods. */
|
||||
Map<std::string, DoubleGetter> float_uniforms_;
|
||||
Map<std::string, BoolGetter> boolean_uniforms_;
|
||||
Map<std::string, Float3Getter> vector_uniforms_;
|
||||
|
||||
/* Maps that associates the name of uniform buffer objects with a getter function that returns
|
||||
* its values. Initialized in the respective addUniform() methods. */
|
||||
Map<std::string, VectorFloatGetter> float_buffers_;
|
||||
Map<std::string, VectorIntGetter> int_buffers_;
|
||||
|
||||
/* A map that associates the name of uniform buffer objects with a getter functions that returns
|
||||
* its number of elements. Initialized in the respective addUniform() methods. */
|
||||
Map<std::string, SizeGetter> buffers_sizes_;
|
||||
|
||||
/* A map that associates the name of a sampler with its corresponding texture. Initialized in the
|
||||
* addTexture() and add3DTexture() methods. */
|
||||
Map<std::string, GPUTexture *> textures_;
|
||||
|
||||
/* A vector set that stores the names of all the resources used by the shader. This is used to:
|
||||
* 1. Check for name collisions when adding new resources.
|
||||
* 2. Store the resource names throughout the construction of the shader since the
|
||||
* ShaderCreateInfo class only stores references to resources names. */
|
||||
VectorSet<std::unique_ptr<std::string>> resource_names_;
|
||||
|
||||
/* A vectors that stores the created uniform buffers when bind_shader_and_resources() is called,
|
||||
* so that they can be properly unbound and freed in the unbind_shader_and_resources() method. */
|
||||
Vector<GPUUniformBuf *> uniform_buffers_;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/* A stub implementation in case OCIO is disabled at build time. */
|
||||
class GPUShaderCreator {
|
||||
public:
|
||||
GPUShader *bind_shader_and_resources()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void unbind_shader_and_resources() {}
|
||||
|
||||
const char *input_sampler_name()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *output_image_name()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader.
|
||||
*/
|
||||
|
||||
OCIOColorSpaceConversionShader::OCIOColorSpaceConversionShader(std::string source,
|
||||
std::string target)
|
||||
{
|
||||
#if defined(WITH_OCIO)
|
||||
/* Get a GPU processor that transforms the source color space to the target color space. */
|
||||
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
|
||||
OCIO::ConstProcessorRcPtr processor = config->getProcessor(source.c_str(), target.c_str());
|
||||
OCIO::ConstGPUProcessorRcPtr gpu_processor = processor->getDefaultGPUProcessor();
|
||||
|
||||
/* Create a GPU shader creator and construct it based on the transforms in the default GPU
|
||||
* processor. */
|
||||
shader_creator_ = GPUShaderCreator::Create();
|
||||
auto ocio_shader_creator = std::static_pointer_cast<OCIO::GpuShaderCreator>(shader_creator_);
|
||||
gpu_processor->extractGpuShaderInfo(ocio_shader_creator);
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUShader *OCIOColorSpaceConversionShader::bind_shader_and_resources()
|
||||
{
|
||||
return shader_creator_->bind_shader_and_resources();
|
||||
}
|
||||
|
||||
void OCIOColorSpaceConversionShader::unbind_shader_and_resources()
|
||||
{
|
||||
shader_creator_->unbind_shader_and_resources();
|
||||
}
|
||||
|
||||
const char *OCIOColorSpaceConversionShader::input_sampler_name()
|
||||
{
|
||||
return shader_creator_->input_sampler_name();
|
||||
}
|
||||
|
||||
const char *OCIOColorSpaceConversionShader::output_image_name()
|
||||
{
|
||||
return shader_creator_->output_image_name();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Container.
|
||||
*/
|
||||
|
||||
void OCIOColorSpaceConversionShaderContainer::reset()
|
||||
{
|
||||
/* First, delete all resources that are no longer needed. */
|
||||
map_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
/* Second, reset the needed status of the remaining resources to false to ready them to track
|
||||
* their needed status for the next evaluation. */
|
||||
for (auto &value : map_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
OCIOColorSpaceConversionShader &OCIOColorSpaceConversionShaderContainer::get(std::string source,
|
||||
std::string target)
|
||||
{
|
||||
#if defined(WITH_OCIO)
|
||||
/* Use the config cache ID in the cache key in case the configuration changed at runtime. */
|
||||
std::string config_cache_id = OCIO::GetCurrentConfig()->getCacheID();
|
||||
#else
|
||||
std::string config_cache_id;
|
||||
#endif
|
||||
|
||||
const OCIOColorSpaceConversionShaderKey key(source, target, config_cache_id);
|
||||
|
||||
OCIOColorSpaceConversionShader &shader = *map_.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<OCIOColorSpaceConversionShader>(source, target); });
|
||||
|
||||
shader.needed = true;
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -12,6 +12,7 @@ void StaticCacheManager::reset()
|
|||
cached_textures.reset();
|
||||
cached_masks.reset();
|
||||
smaa_precomputed_textures.reset();
|
||||
ocio_color_space_conversion_shaders.reset();
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
/* OCIOMain will be dynamically generated in the OCIOColorSpaceConversionShader class and appended
|
||||
* at the end of this file, so forward declare it. Such forward declarations are not supported nor
|
||||
* needed on Metal. */
|
||||
#if !defined(GPU_METAL)
|
||||
vec4 OCIOMain(vec4 inPixel);
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
imageStore(output_img, texel, OCIOMain(texture_load(input_tx, texel)));
|
||||
}
|
|
@ -1920,9 +1920,19 @@ void FONT_OT_selection_set(struct wmOperatorType *ot)
|
|||
|
||||
static int font_select_word_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
move_cursor(C, NEXT_CHAR, false);
|
||||
move_cursor(C, PREV_WORD, false);
|
||||
move_cursor(C, NEXT_WORD, true);
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
Curve *cu = obedit->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
|
||||
BLI_str_cursor_step_bounds_utf32(ef->textbuf, ef->len, ef->pos, &ef->selstart, &ef->selend);
|
||||
ef->pos = ef->selend;
|
||||
|
||||
/* XXX: Text object selection start is 1-based, unlike text processing elsewhere in Blender. */
|
||||
ef->selstart += 1;
|
||||
|
||||
font_select_update_primary_clipboard(obedit);
|
||||
text_update_edited(C, obedit, FO_CURS);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
|
|
@ -3730,8 +3730,11 @@ static void ui_do_but_textedit(
|
|||
|
||||
/* only select a word in button if there was no selection before */
|
||||
if (event->val == KM_DBL_CLICK && had_selection == false) {
|
||||
ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_DELIM);
|
||||
ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_DELIM);
|
||||
int selsta, selend;
|
||||
BLI_str_cursor_step_bounds_utf8(data->str, strlen(data->str), but->pos, &selsta, &selend);
|
||||
but->pos = (short)selend;
|
||||
but->selsta = (short)selsta;
|
||||
but->selend = (short)selend;
|
||||
retval = WM_UI_HANDLER_BREAK;
|
||||
changed = true;
|
||||
}
|
||||
|
|
|
@ -386,6 +386,11 @@ static int hide_show_exec(bContext *C, wmOperator *op)
|
|||
BKE_pbvh_update_hide_attributes_from_mesh(pbvh);
|
||||
}
|
||||
|
||||
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
||||
if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) {
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
|
||||
ED_region_tag_redraw(region);
|
||||
|
||||
|
|
|
@ -3029,7 +3029,10 @@ static void calc_brush_local_mat(const float rotation,
|
|||
|
||||
/* Square tips should scale by square root of 2. */
|
||||
if (BKE_brush_has_cube_tip(cache->brush, PAINT_MODE_SCULPT)) {
|
||||
radius += (radius * M_SQRT2 - radius) * (1.0f - cache->brush->tip_roundness);
|
||||
radius += (radius / M_SQRT2 - radius) * cache->brush->tip_roundness;
|
||||
}
|
||||
else {
|
||||
radius /= M_SQRT2;
|
||||
}
|
||||
|
||||
normalize_m4(mat);
|
||||
|
@ -3670,7 +3673,10 @@ static void do_brush_action(Sculpt *sd,
|
|||
sd, ob, nodes, ss->cache->cloth_sim, ss->cache->location, FLT_MAX);
|
||||
}
|
||||
|
||||
bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN;
|
||||
bool invert = ss->cache->pen_flip || ss->cache->invert;
|
||||
if (brush->flag & BRUSH_DIR_IN) {
|
||||
invert = !invert;
|
||||
}
|
||||
|
||||
/* Apply one type of brush action. */
|
||||
switch (brush->sculpt_tool) {
|
||||
|
|
|
@ -1258,9 +1258,7 @@ static int console_selectword_invoke(bContext *C, wmOperator *UNUSED(op), const
|
|||
if (console_line_column_from_index(sc, pos, &cl, &offset, &n)) {
|
||||
int sel[2] = {n, n};
|
||||
|
||||
BLI_str_cursor_step_utf8(cl->line, cl->len, &sel[0], STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, true);
|
||||
|
||||
BLI_str_cursor_step_utf8(cl->line, cl->len, &sel[1], STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, true);
|
||||
BLI_str_cursor_step_bounds_utf8(cl->line, cl->len, n, &sel[0], &sel[1]);
|
||||
|
||||
sel[0] = offset - sel[0];
|
||||
sel[1] = offset - sel[1];
|
||||
|
|
|
@ -494,6 +494,9 @@ static void outliner_space_blend_read_lib(BlendLibReader *reader,
|
|||
if (TSE_IS_REAL_ID(tselem)) {
|
||||
BLO_read_id_address(reader, nullptr, &tselem->id);
|
||||
}
|
||||
else {
|
||||
tselem->id = nullptr;
|
||||
}
|
||||
}
|
||||
/* rebuild hash table, because it depends on ids too */
|
||||
space_outliner->storeflag |= SO_TREESTORE_REBUILD;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_string_cursor_utf8.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -1577,11 +1578,9 @@ void TEXT_OT_select_line(wmOperatorType *ot)
|
|||
static int text_select_word_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Text *text = CTX_data_edit_text(C);
|
||||
/* don't advance cursor before stepping */
|
||||
const bool use_init_step = false;
|
||||
|
||||
txt_jump_left(text, false, use_init_step);
|
||||
txt_jump_right(text, true, use_init_step);
|
||||
BLI_str_cursor_step_bounds_utf8(
|
||||
text->curl->line, text->curl->len, text->selc, &text->curc, &text->selc);
|
||||
|
||||
text_update_cursor_moved(C);
|
||||
text_select_update_primary_clipboard(text);
|
||||
|
|
|
@ -101,10 +101,11 @@ static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data)
|
|||
{
|
||||
ID *id_self = cb_data->id_self;
|
||||
ID **id_pointer = cb_data->id_pointer;
|
||||
BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0);
|
||||
BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED) != 0);
|
||||
|
||||
ID *id = *id_pointer;
|
||||
if (id != nullptr && !ID_IS_LINKED(id) && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) {
|
||||
if (id != nullptr && !ID_IS_LINKED(id) && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED) == 0)
|
||||
{
|
||||
bool do_stop_iter = true;
|
||||
if (GS(id_self->name) == ID_OB) {
|
||||
Object *ob_self = (Object *)id_self;
|
||||
|
@ -234,7 +235,7 @@ static void memfile_undosys_step_decode(struct bContext *C,
|
|||
* data-blocks, at least COW evaluated copies need to be updated... */
|
||||
ID *id = nullptr;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
if (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) {
|
||||
if (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED) {
|
||||
BKE_library_foreach_ID_link(
|
||||
bmain, id, memfile_undosys_step_id_reused_cb, nullptr, IDWALK_READONLY);
|
||||
}
|
||||
|
@ -282,7 +283,7 @@ static void memfile_undosys_step_decode(struct bContext *C,
|
|||
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
/* Clear temporary tag. */
|
||||
id->tag &= ~(LIB_TAG_UNDO_OLD_ID_REUSED | LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE);
|
||||
id->tag &= ~(LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED | LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE);
|
||||
|
||||
/* We only start accumulating from this point, any tags set up to here
|
||||
* are already part of the current undo state. This is done in a second
|
||||
|
|
|
@ -1184,6 +1184,9 @@ static int64_t pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
MutableSpan<uv_phi> r_phis,
|
||||
rctf *r_extent)
|
||||
{
|
||||
if (params.shape_method == ED_UVPACK_SHAPE_AABB) {
|
||||
return 0; /* Not yet supported. */
|
||||
}
|
||||
blender::Array<uv_phi> phis(r_phis.size());
|
||||
Occupancy occupancy(guess_initial_scale(islands, scale, margin));
|
||||
rctf extent = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
@ -1446,7 +1449,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
});
|
||||
}
|
||||
|
||||
/* Partition `islands`, largest islands will go to a slow packer, the rest alpaca_turbo.
|
||||
/* Partition `islands`, largest islands will go to a slow packer, the rest the fast packer.
|
||||
* See discussion above for details. */
|
||||
int64_t alpaca_cutoff = 1024; /* Regular situation, pack `32 * 32` islands with slow packer. */
|
||||
int64_t alpaca_cutoff_fast = 81; /* Reduce problem size, only `N = 9 * 9` with slow packer. */
|
||||
|
@ -1455,53 +1458,39 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
alpaca_cutoff = alpaca_cutoff_fast;
|
||||
}
|
||||
}
|
||||
int64_t max_box_pack = std::min(alpaca_cutoff, islands.size());
|
||||
|
||||
Span<UVAABBIsland *> slow_aabbs = aabbs.as_span().take_front(
|
||||
std::min(alpaca_cutoff, islands.size()));
|
||||
rctf extent = {0.0f, 1e30f, 0.0f, 1e30f};
|
||||
|
||||
if (all_can_translate) {
|
||||
pack_islands_fast(0,
|
||||
aabbs.as_span().take_front(max_box_pack),
|
||||
all_can_rotate,
|
||||
params.target_aspect_y,
|
||||
r_phis,
|
||||
&extent);
|
||||
pack_islands_fast(0, slow_aabbs, all_can_rotate, params.target_aspect_y, r_phis, &extent);
|
||||
}
|
||||
|
||||
if (all_can_translate) {
|
||||
pack_islands_optimal_pack(aabbs.as_span().take_front(max_box_pack), params, r_phis, &extent);
|
||||
pack_islands_optimal_pack(slow_aabbs, params, r_phis, &extent);
|
||||
}
|
||||
|
||||
/* Call box_pack_2d (slow for large N.) */
|
||||
if (all_can_translate) {
|
||||
pack_island_box_pack_2d(aabbs.as_span().take_front(max_box_pack), params, r_phis, &extent);
|
||||
pack_island_box_pack_2d(slow_aabbs, params, r_phis, &extent);
|
||||
}
|
||||
|
||||
/* Call xatlas (slow for large N.) */
|
||||
switch (params.shape_method) {
|
||||
case ED_UVPACK_SHAPE_CONVEX:
|
||||
case ED_UVPACK_SHAPE_CONCAVE:
|
||||
max_box_pack = pack_island_xatlas(aabbs.as_span().take_front(max_box_pack),
|
||||
islands,
|
||||
scale,
|
||||
margin,
|
||||
params,
|
||||
r_phis,
|
||||
&extent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
int64_t max_xatlas = pack_island_xatlas(
|
||||
slow_aabbs, islands, scale, margin, params, r_phis, &extent);
|
||||
if (max_xatlas) {
|
||||
slow_aabbs = aabbs.as_span().take_front(max_xatlas);
|
||||
}
|
||||
|
||||
/* At this stage, `max_u` and `max_v` contain the box_pack/xatlas UVs. */
|
||||
/* At this stage, `extent` contains the optimal/box_pack/xatlas UVs. */
|
||||
|
||||
if (all_can_rotate) {
|
||||
rotate_inside_square(
|
||||
aabbs.as_span().take_front(max_box_pack), islands, params, scale, margin, r_phis, &extent);
|
||||
rotate_inside_square(slow_aabbs, islands, params, scale, margin, r_phis, &extent);
|
||||
}
|
||||
|
||||
/* Call fast packer for remaining islands. */
|
||||
pack_islands_fast(max_box_pack, aabbs, all_can_rotate, params.target_aspect_y, r_phis, &extent);
|
||||
pack_islands_fast(
|
||||
slow_aabbs.size(), aabbs, all_can_rotate, params.target_aspect_y, r_phis, &extent);
|
||||
|
||||
return get_aspect_scaled_extent(extent, params);
|
||||
}
|
||||
|
|
|
@ -441,6 +441,11 @@ struct GPUSource {
|
|||
int64_t cursor = -1;
|
||||
StringRef func_return_type, func_name, func_args;
|
||||
while (function_parse(input, cursor, func_return_type, func_name, func_args)) {
|
||||
/* Main functions needn't be handled because they are the entry point of the shader. */
|
||||
if (func_name == "main") {
|
||||
continue;
|
||||
}
|
||||
|
||||
GPUFunction *func = MEM_new<GPUFunction>(__func__);
|
||||
func_name.copy(func->name, sizeof(func->name));
|
||||
func->source = reinterpret_cast<void *>(this);
|
||||
|
|
|
@ -1227,9 +1227,12 @@ static IK_Scene *convert_tree(
|
|||
if (pchan->parent) {
|
||||
sub_v3_v3v3(start, pchan->pose_head, pchan->parent->pose_tail);
|
||||
}
|
||||
else {
|
||||
else if (ikparam->flag & ITASC_TRANSLATE_ROOT_BONES) {
|
||||
start[0] = start[1] = start[2] = 0.0f;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(start, pchan->pose_head);
|
||||
}
|
||||
invert_m3_m3(iR_parmat, R_parmat);
|
||||
normalize_m3(iR_parmat);
|
||||
mul_m3_v3(iR_parmat, start);
|
||||
|
|
|
@ -855,11 +855,13 @@ enum {
|
|||
LIB_TAG_NEED_LINK = 1 << 16,
|
||||
/**
|
||||
* ID is being re-used from the old Main (instead of read from memfile), during memfile undo
|
||||
* processing.
|
||||
* processing, because it was detected as unchanged.
|
||||
*
|
||||
* \note: Also means that such ID does not need to be lib-linked during undo readfile process.
|
||||
*
|
||||
* RESET_AFTER_USE
|
||||
*/
|
||||
LIB_TAG_UNDO_OLD_ID_REUSED = 1 << 17,
|
||||
LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED = 1 << 17,
|
||||
/**
|
||||
* ID has be re-read in-place, the ID address is the same as in the old BMain, but the content is
|
||||
* different.
|
||||
|
|
|
@ -567,6 +567,10 @@ typedef enum eItasc_Flags {
|
|||
ITASC_INITIAL_REITERATION = (1 << 1),
|
||||
ITASC_REITERATION = (1 << 2),
|
||||
ITASC_SIMULATION = (1 << 3),
|
||||
|
||||
/* Set this flag to always translate root bones (i.e. bones without a parent) to (0, 0, 0).
|
||||
* This was the pre-3.6 behaviour, and this flag was introduced for backward compatibility. */
|
||||
ITASC_TRANSLATE_ROOT_BONES = (1 << 4),
|
||||
} eItasc_Flags;
|
||||
|
||||
/* bItasc->solver */
|
||||
|
|
|
@ -1561,9 +1561,9 @@ static void rna_def_armature(BlenderRNA *brna)
|
|||
RNA_def_property_boolean_sdna(prop, NULL, "layer_protected", 1);
|
||||
RNA_def_property_array(prop, 32);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Layer Proxy Protection",
|
||||
"Protected layers in Proxy Instances are restored to Proxy settings "
|
||||
"on file reload and undo");
|
||||
"Layer Override Protection",
|
||||
"Protected layers in overridden instances are restored to "
|
||||
"their original settings on file reload and undo");
|
||||
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
||||
|
||||
/* flag */
|
||||
|
|
|
@ -3484,7 +3484,10 @@ static void rna_def_brush(BlenderRNA *brna)
|
|||
|
||||
prop = RNA_def_property(srna, "use_plane_trim", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_PLANE_TRIM);
|
||||
RNA_def_property_ui_text(prop, "Use Plane Trim", "Enable Plane Trim");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Use Plane Trim",
|
||||
"Limit the distance from the offset plane that a vertex can be affected");
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_frontface", PROP_BOOLEAN, PROP_NONE);
|
||||
|
|
|
@ -1018,7 +1018,7 @@ static void rna_def_path(BlenderRNA *UNUSED(brna), StructRNA *srna)
|
|||
|
||||
prop = RNA_def_property(srna, "use_path_follow", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_FOLLOW);
|
||||
RNA_def_property_ui_text(prop, "Follow", "Make curve path children to rotate along the path");
|
||||
RNA_def_property_ui_text(prop, "Follow", "Make curve path children rotate along the path");
|
||||
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
|
||||
|
||||
prop = RNA_def_property(srna, "use_path_clamp", PROP_BOOLEAN, PROP_NONE);
|
||||
|
@ -1034,7 +1034,7 @@ static void rna_def_path(BlenderRNA *UNUSED(brna), StructRNA *srna)
|
|||
RNA_def_property_ui_text(prop,
|
||||
"Stretch",
|
||||
"Option for curve-deform: "
|
||||
"make deformed child to stretch along entire path");
|
||||
"make deformed child stretch along entire path");
|
||||
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
|
||||
|
||||
prop = RNA_def_property(srna, "use_deform_bounds", PROP_BOOLEAN, PROP_NONE);
|
||||
|
@ -1050,7 +1050,8 @@ static void rna_def_path(BlenderRNA *UNUSED(brna), StructRNA *srna)
|
|||
RNA_def_property_ui_text(prop,
|
||||
"Radius",
|
||||
"Option for paths and curve-deform: "
|
||||
"apply the curve radius with path following it and deforming");
|
||||
"apply the curve radius to objects following it "
|
||||
"and to deformed objects");
|
||||
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
|
||||
}
|
||||
|
||||
|
|
|
@ -574,7 +574,7 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro
|
|||
|
||||
for (a = 0; kwlist[a]; a++) {
|
||||
if (STREQ(identifier, kwlist[a])) {
|
||||
strcpy(error, "this keyword is reserved by python");
|
||||
strcpy(error, "this keyword is reserved by Python");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -591,7 +591,7 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro
|
|||
|
||||
for (a = 0; kwlist_prop[a]; a++) {
|
||||
if (STREQ(identifier, kwlist_prop[a])) {
|
||||
strcpy(error, "this keyword is reserved by python");
|
||||
strcpy(error, "this keyword is reserved by Python");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -784,7 +784,7 @@ void RNA_struct_free(BlenderRNA *brna, StructRNA *srna)
|
|||
# if 0
|
||||
if (srna->flag & STRUCT_RUNTIME) {
|
||||
if (RNA_struct_py_type_get(srna)) {
|
||||
fprintf(stderr, "%s '%s' freed while holding a python reference.", srna->identifier);
|
||||
fprintf(stderr, "%s '%s' freed while holding a Python reference.", srna->identifier);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
|
|
@ -2148,7 +2148,7 @@ static void rna_def_channeldriver(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Simple Expression",
|
||||
"The scripted expression can be evaluated without using the full python interpreter");
|
||||
"The scripted expression can be evaluated without using the full Python interpreter");
|
||||
|
||||
/* Functions */
|
||||
RNA_api_drivers(srna);
|
||||
|
|
|
@ -2957,7 +2957,7 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
|
|||
|
||||
prop = RNA_def_property(srna, "falloff_curve", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "curfalloff");
|
||||
RNA_def_property_ui_text(prop, "Falloff Curve", "Custom light falloff curve");
|
||||
RNA_def_property_ui_text(prop, "Falloff Curve", "Custom falloff curve");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "center", PROP_FLOAT, PROP_NONE);
|
||||
|
@ -3870,7 +3870,8 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
|
|||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer assigned to the generated strokes");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Layer", "Grease Pencil layer to which assign the generated strokes");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "source_vertex_group", PROP_STRING, PROP_NONE);
|
||||
|
|
|
@ -9958,7 +9958,7 @@ static void rna_def_simulation_state_item(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Attribute Domain",
|
||||
"Attribute domain where the attribute domain is stored in the simulation state");
|
||||
"Attribute domain where the attribute is stored in the simulation state");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationStateItem_update");
|
||||
|
||||
|
|
|
@ -3796,8 +3796,7 @@ static void rna_def_object(BlenderRNA *brna)
|
|||
/* shape keys */
|
||||
prop = RNA_def_property(srna, "show_only_shape_key", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "shapeflag", OB_SHAPE_LOCK);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Shape Key Lock", "Always show the current shape for this object");
|
||||
RNA_def_property_ui_text(prop, "Shape Key Lock", "Only show the active shape at full strength");
|
||||
RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1);
|
||||
RNA_def_property_update(prop, 0, "rna_Object_internal_update_data");
|
||||
|
||||
|
|
|
@ -1512,6 +1512,12 @@ static void rna_def_pose_itasc(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Num Steps", "Divide the frame interval into this many steps");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update");
|
||||
|
||||
prop = RNA_def_property(srna, "translate_root_bones", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", ITASC_TRANSLATE_ROOT_BONES);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Translate Roots", "Translate root (i.e. parentless) bones to the armature origin");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update");
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
|
||||
RNA_def_property_enum_items(prop, prop_itasc_mode_items);
|
||||
|
|
|
@ -376,7 +376,7 @@ static StructRNA *rna_RenderEngine_register(Main *bmain,
|
|||
}
|
||||
|
||||
/* create a new engine type */
|
||||
et = MEM_mallocN(sizeof(RenderEngineType), "python render engine");
|
||||
et = MEM_mallocN(sizeof(RenderEngineType), "Python render engine");
|
||||
memcpy(et, &dummy_et, sizeof(dummy_et));
|
||||
|
||||
et->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, et->idname, &RNA_RenderEngine);
|
||||
|
|
|
@ -3159,7 +3159,7 @@ static void rna_def_function(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"No Self",
|
||||
"Function does not pass itself as an argument (becomes a static method in python)");
|
||||
"Function does not pass itself as an argument (becomes a static method in Python)");
|
||||
|
||||
prop = RNA_def_property(srna, "use_self_type", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
@ -3167,7 +3167,7 @@ static void rna_def_function(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop,
|
||||
"Use Self Type",
|
||||
"Function passes itself type as an argument (becomes a class method "
|
||||
"in python if use_self is false)");
|
||||
"in Python if use_self is false)");
|
||||
}
|
||||
|
||||
static void rna_def_number_property(StructRNA *srna, PropertyType type)
|
||||
|
|
|
@ -7956,7 +7956,7 @@ void RNA_def_scene(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Current Frame",
|
||||
"Current frame, to update animation data from python frame_set() instead");
|
||||
"Current frame, to update animation data from Python frame_set() instead");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_FRAME, "rna_Scene_frame_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_subframe", PROP_FLOAT, PROP_TIME);
|
||||
|
|
|
@ -6112,7 +6112,7 @@ static void rna_def_space_text(BlenderRNA *brna)
|
|||
|
||||
prop = RNA_def_property(srna, "use_live_edit", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "live_edit", 1);
|
||||
RNA_def_property_ui_text(prop, "Live Edit", "Run python while editing");
|
||||
RNA_def_property_ui_text(prop, "Live Edit", "Run Python while editing");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TEXT, NULL);
|
||||
|
||||
/* find */
|
||||
|
@ -6558,7 +6558,7 @@ static void rna_def_space_console(BlenderRNA *brna)
|
|||
|
||||
srna = RNA_def_struct(brna, "SpaceConsole", "Space");
|
||||
RNA_def_struct_sdna(srna, "SpaceConsole");
|
||||
RNA_def_struct_ui_text(srna, "Space Console", "Interactive python console");
|
||||
RNA_def_struct_ui_text(srna, "Space Console", "Interactive Python console");
|
||||
|
||||
/* display */
|
||||
prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_NONE); /* copied from text editor */
|
||||
|
|
|
@ -362,7 +362,7 @@ static StructRNA *rna_Panel_register(Main *bmain,
|
|||
description_size = strlen(_panel_descr) + 1;
|
||||
over_alloc += description_size;
|
||||
}
|
||||
pt = MEM_callocN(sizeof(PanelType) + over_alloc, "python buttons panel");
|
||||
pt = MEM_callocN(sizeof(PanelType) + over_alloc, "Python buttons panel");
|
||||
memcpy(pt, &dummy_pt, sizeof(dummy_pt));
|
||||
|
||||
if (_panel_descr[0]) {
|
||||
|
@ -867,7 +867,7 @@ static StructRNA *rna_Header_register(Main *bmain,
|
|||
}
|
||||
|
||||
/* create a new header type */
|
||||
ht = MEM_mallocN(sizeof(HeaderType), "python buttons header");
|
||||
ht = MEM_mallocN(sizeof(HeaderType), "Python buttons header");
|
||||
memcpy(ht, &dummy_ht, sizeof(dummy_ht));
|
||||
|
||||
ht->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, ht->idname, &RNA_Header);
|
||||
|
@ -1024,7 +1024,7 @@ static StructRNA *rna_Menu_register(Main *bmain,
|
|||
over_alloc += description_size;
|
||||
}
|
||||
|
||||
mt = MEM_callocN(sizeof(MenuType) + over_alloc, "python buttons menu");
|
||||
mt = MEM_callocN(sizeof(MenuType) + over_alloc, "Python buttons menu");
|
||||
memcpy(mt, &dummy_mt, sizeof(dummy_mt));
|
||||
|
||||
if (_menu_descr[0]) {
|
||||
|
|
|
@ -6315,14 +6315,14 @@ static void rna_def_userdef_script_directory_collection(BlenderRNA *brna, Proper
|
|||
|
||||
func = RNA_def_function(srna, "new", "rna_userdef_script_directory_new");
|
||||
RNA_def_function_flag(func, FUNC_NO_SELF);
|
||||
RNA_def_function_ui_description(func, "Add a new python script directory");
|
||||
RNA_def_function_ui_description(func, "Add a new Python script directory");
|
||||
/* return type */
|
||||
parm = RNA_def_pointer(func, "script_directory", "ScriptDirectory", "", "");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "remove", "rna_userdef_script_directory_remove");
|
||||
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_REPORTS);
|
||||
RNA_def_function_ui_description(func, "Remove a python script directory");
|
||||
RNA_def_function_ui_description(func, "Remove a Python script directory");
|
||||
parm = RNA_def_pointer(func, "script_directory", "ScriptDirectory", "", "");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
|
||||
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
|
||||
|
@ -6631,7 +6631,7 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
|
|||
prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Override Templates", "Enable library override template in the python API");
|
||||
prop, "Override Templates", "Enable library override template in the Python API");
|
||||
|
||||
prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1);
|
||||
|
|
|
@ -324,6 +324,6 @@ def main():
|
|||
if __name__ == '__main__':
|
||||
import sys
|
||||
if sys.version_info.major < 3:
|
||||
print("Incorrect python version, use Python 3 or newer!")
|
||||
print("Incorrect Python version, use Python 3 or newer!")
|
||||
else:
|
||||
main()
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* \ingroup cmpnodes
|
||||
*/
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
@ -16,13 +14,23 @@
|
|||
|
||||
#include "IMB_colormanagement.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_ocio_color_space_conversion_shader.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
namespace blender::nodes::node_composite_convert_color_space_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeConvertColorSpace)
|
||||
|
||||
static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Color>(N_("Image"))
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.compositor_domain_priority(0);
|
||||
b.add_output<decl::Color>(N_("Image"));
|
||||
}
|
||||
|
||||
|
@ -59,8 +67,79 @@ class ConvertColorSpaceOperation : public NodeOperation {
|
|||
|
||||
void execute() override
|
||||
{
|
||||
get_input("Image").pass_through(get_result("Image"));
|
||||
context().set_info_message("Viewport compositor setup not fully supported");
|
||||
Result &input_image = get_input("Image");
|
||||
Result &output_image = get_result("Image");
|
||||
if (is_identity()) {
|
||||
input_image.pass_through(output_image);
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_image.is_single_value()) {
|
||||
execute_single();
|
||||
return;
|
||||
}
|
||||
|
||||
const char *source = node_storage(bnode()).from_color_space;
|
||||
const char *target = node_storage(bnode()).to_color_space;
|
||||
|
||||
OCIOColorSpaceConversionShader &ocio_shader =
|
||||
context().cache_manager().ocio_color_space_conversion_shaders.get(source, target);
|
||||
|
||||
GPUShader *shader = ocio_shader.bind_shader_and_resources();
|
||||
|
||||
/* A null shader indicates that the conversion shader is just a stub implementation since OCIO
|
||||
* is disabled at compile time, so pass the input through in that case. */
|
||||
if (!shader) {
|
||||
input_image.pass_through(output_image);
|
||||
return;
|
||||
}
|
||||
|
||||
input_image.bind_as_texture(shader, ocio_shader.input_sampler_name());
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
output_image.allocate_texture(domain);
|
||||
output_image.bind_as_image(shader, ocio_shader.output_image_name());
|
||||
|
||||
compute_dispatch_threads_at_least(shader, domain.size);
|
||||
|
||||
input_image.unbind_as_texture();
|
||||
output_image.unbind_as_image();
|
||||
ocio_shader.unbind_shader_and_resources();
|
||||
}
|
||||
|
||||
void execute_single()
|
||||
{
|
||||
const char *source = node_storage(bnode()).from_color_space;
|
||||
const char *target = node_storage(bnode()).to_color_space;
|
||||
ColormanageProcessor *color_processor = IMB_colormanagement_colorspace_processor_new(source,
|
||||
target);
|
||||
|
||||
Result &input_image = get_input("Image");
|
||||
float4 color = input_image.get_color_value();
|
||||
|
||||
IMB_colormanagement_processor_apply_pixel(color_processor, color, 3);
|
||||
IMB_colormanagement_processor_free(color_processor);
|
||||
|
||||
Result &output_image = get_result("Image");
|
||||
output_image.allocate_single_value();
|
||||
output_image.set_color_value(color);
|
||||
}
|
||||
|
||||
bool is_identity()
|
||||
{
|
||||
const char *source = node_storage(bnode()).from_color_space;
|
||||
const char *target = node_storage(bnode()).to_color_space;
|
||||
|
||||
if (STREQ(source, target)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Data color spaces ignore any color transformation that gets applied to them. */
|
||||
if (IMB_colormanagement_space_name_is_data(source)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -85,8 +164,6 @@ void register_node_type_cmp_convert_color_space(void)
|
|||
node_type_storage(
|
||||
&ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.realtime_compositor_unsupported_message = N_(
|
||||
"Node not supported in the Viewport compositor");
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -558,7 +558,7 @@ PyDoc_STRVAR(bpy_bmlayercollection_keys_doc,
|
|||
".. method:: keys()\n"
|
||||
"\n"
|
||||
" Return the identifiers of collection members\n"
|
||||
" (matching pythons dict.keys() functionality).\n"
|
||||
" (matching Python's dict.keys() functionality).\n"
|
||||
"\n"
|
||||
" :return: the identifiers for each member of this collection.\n"
|
||||
" :rtype: list of strings\n");
|
||||
|
@ -593,7 +593,7 @@ PyDoc_STRVAR(bpy_bmlayercollection_items_doc,
|
|||
".. method:: items()\n"
|
||||
"\n"
|
||||
" Return the identifiers of collection members\n"
|
||||
" (matching pythons dict.items() functionality).\n"
|
||||
" (matching Python's dict.items() functionality).\n"
|
||||
"\n"
|
||||
" :return: (key, value) pairs for each member of this collection.\n"
|
||||
" :rtype: list of tuples\n");
|
||||
|
@ -628,7 +628,7 @@ PyDoc_STRVAR(bpy_bmlayercollection_values_doc,
|
|||
".. method:: values()\n"
|
||||
"\n"
|
||||
" Return the values of collection\n"
|
||||
" (matching pythons dict.values() functionality).\n"
|
||||
" (matching Python's dict.values() functionality).\n"
|
||||
"\n"
|
||||
" :return: the members of this collection.\n"
|
||||
" :rtype: list\n");
|
||||
|
@ -660,7 +660,7 @@ PyDoc_STRVAR(bpy_bmlayercollection_get_doc,
|
|||
".. method:: get(key, default=None)\n"
|
||||
"\n"
|
||||
" Returns the value of the layer matching the key or default\n"
|
||||
" when not found (matches pythons dictionary function of the same name).\n"
|
||||
" when not found (matches Python's dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :arg key: The key associated with the layer.\n"
|
||||
" :type key: string\n"
|
||||
|
@ -929,7 +929,7 @@ PyDoc_STRVAR(bpy_bmlayeraccess_type_doc, "Exposes custom-data layer attributes."
|
|||
|
||||
PyDoc_STRVAR(bpy_bmlayercollection_type_doc,
|
||||
"Gives access to a collection of custom-data layers of the same type and behaves "
|
||||
"like python dictionaries, "
|
||||
"like Python dictionaries, "
|
||||
"except for the ability to do list like index access.");
|
||||
|
||||
PyDoc_STRVAR(bpy_bmlayeritem_type_doc,
|
||||
|
|
|
@ -600,7 +600,7 @@ PyDoc_STRVAR(bpy_bmdeformvert_keys_doc,
|
|||
".. method:: keys()\n"
|
||||
"\n"
|
||||
" Return the group indices used by this vertex\n"
|
||||
" (matching pythons dict.keys() functionality).\n"
|
||||
" (matching Python's dict.keys() functionality).\n"
|
||||
"\n"
|
||||
" :return: the deform group this vertex uses\n"
|
||||
" :rtype: list of ints\n");
|
||||
|
@ -622,7 +622,7 @@ PyDoc_STRVAR(bpy_bmdeformvert_values_doc,
|
|||
".. method:: values()\n"
|
||||
"\n"
|
||||
" Return the weights of the deform vertex\n"
|
||||
" (matching pythons dict.values() functionality).\n"
|
||||
" (matching Python's dict.values() functionality).\n"
|
||||
"\n"
|
||||
" :return: The weights that influence this vertex\n"
|
||||
" :rtype: list of floats\n");
|
||||
|
@ -644,7 +644,7 @@ PyDoc_STRVAR(bpy_bmdeformvert_items_doc,
|
|||
".. method:: items()\n"
|
||||
"\n"
|
||||
" Return (group, weight) pairs for this vertex\n"
|
||||
" (matching pythons dict.items() functionality).\n"
|
||||
" (matching Python's dict.items() functionality).\n"
|
||||
"\n"
|
||||
" :return: (key, value) pairs for each deform weight of this vertex.\n"
|
||||
" :rtype: list of tuples\n");
|
||||
|
@ -669,7 +669,7 @@ PyDoc_STRVAR(bpy_bmdeformvert_get_doc,
|
|||
".. method:: get(key, default=None)\n"
|
||||
"\n"
|
||||
" Returns the deform weight matching the key or default\n"
|
||||
" when not found (matches pythons dictionary function of the same name).\n"
|
||||
" when not found (matches Python's dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :arg key: The key associated with deform weight.\n"
|
||||
" :type key: int\n"
|
||||
|
|
|
@ -1553,7 +1553,7 @@ static PyObject *BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value)
|
|||
PyDoc_STRVAR(BPy_IDGroup_to_dict_doc,
|
||||
".. method:: to_dict()\n"
|
||||
"\n"
|
||||
" Return a purely python version of the group.\n");
|
||||
" Return a purely Python version of the group.\n");
|
||||
static PyObject *BPy_IDGroup_to_dict(BPy_IDProperty *self)
|
||||
{
|
||||
return BPy_IDGroup_MapDataToPy(self->prop);
|
||||
|
|
|
@ -607,7 +607,7 @@ void PyC_LineSpit(void)
|
|||
|
||||
/* NOTE: allow calling from outside python (RNA). */
|
||||
if (!PyC_IsInterpreterActive()) {
|
||||
fprintf(stderr, "python line lookup failed, interpreter inactive\n");
|
||||
fprintf(stderr, "Python line lookup failed, interpreter inactive\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -621,7 +621,7 @@ void PyC_StackSpit(void)
|
|||
{
|
||||
/* NOTE: allow calling from outside python (RNA). */
|
||||
if (!PyC_IsInterpreterActive()) {
|
||||
fprintf(stderr, "python line lookup failed, interpreter inactive\n");
|
||||
fprintf(stderr, "Python line lookup failed, interpreter inactive\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -410,7 +410,7 @@ void BPY_python_start(bContext *C, int argc, const char **argv)
|
|||
else {
|
||||
/* Set to `sys.executable = None` below (we can't do before Python is initialized). */
|
||||
fprintf(stderr,
|
||||
"Unable to find the python binary, "
|
||||
"Unable to find the Python binary, "
|
||||
"the multiprocessing module may not be functional!\n");
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +427,7 @@ void BPY_python_start(bContext *C, int argc, const char **argv)
|
|||
if (strchr(py_path_bundle, ':')) {
|
||||
fprintf(stderr,
|
||||
"Warning! Blender application is located in a path containing ':' or '/' chars\n"
|
||||
"This may make python import function fail\n");
|
||||
"This may make Python import function fail\n");
|
||||
}
|
||||
# endif /* __APPLE__ */
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ PyDoc_STRVAR(
|
|||
" :type str_ref_unit: string or None\n"
|
||||
" :return: The converted/interpreted value.\n"
|
||||
" :rtype: float\n"
|
||||
" :raises ValueError: if conversion fails to generate a valid python float value.\n");
|
||||
" :raises ValueError: if conversion fails to generate a valid Python float value.\n");
|
||||
static PyObject *bpyunits_to_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
|
||||
{
|
||||
char *usys_str = NULL, *ucat_str = NULL, *inpt = NULL, *uref = NULL;
|
||||
|
@ -243,7 +243,7 @@ PyDoc_STRVAR(bpyunits_to_string_doc,
|
|||
" :type compatible_unit: bool\n"
|
||||
" :return: The converted string.\n"
|
||||
" :rtype: str\n"
|
||||
" :raises ValueError: if conversion fails to generate a valid python string.\n");
|
||||
" :raises ValueError: if conversion fails to generate a valid Python string.\n");
|
||||
static PyObject *bpyunits_to_string(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
|
||||
{
|
||||
char *usys_str = NULL, *ucat_str = NULL;
|
||||
|
|
|
@ -475,7 +475,7 @@ static PyObject *Vector_resize(VectorObject *self, PyObject *value)
|
|||
if (self->flag & BASE_MATH_FLAG_IS_WRAP) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Vector.resize(): "
|
||||
"cannot resize wrapped data - only python vectors");
|
||||
"cannot resize wrapped data - only Python vectors");
|
||||
return NULL;
|
||||
}
|
||||
if (self->cb_user) {
|
||||
|
@ -559,7 +559,7 @@ static PyObject *Vector_resize_2d(VectorObject *self)
|
|||
if (self->flag & BASE_MATH_FLAG_IS_WRAP) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Vector.resize_2d(): "
|
||||
"cannot resize wrapped data - only python vectors");
|
||||
"cannot resize wrapped data - only Python vectors");
|
||||
return NULL;
|
||||
}
|
||||
if (self->cb_user) {
|
||||
|
@ -590,7 +590,7 @@ static PyObject *Vector_resize_3d(VectorObject *self)
|
|||
if (self->flag & BASE_MATH_FLAG_IS_WRAP) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Vector.resize_3d(): "
|
||||
"cannot resize wrapped data - only python vectors");
|
||||
"cannot resize wrapped data - only Python vectors");
|
||||
return NULL;
|
||||
}
|
||||
if (self->cb_user) {
|
||||
|
@ -625,7 +625,7 @@ static PyObject *Vector_resize_4d(VectorObject *self)
|
|||
if (self->flag & BASE_MATH_FLAG_IS_WRAP) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Vector.resize_4d(): "
|
||||
"cannot resize wrapped data - only python vectors");
|
||||
"cannot resize wrapped data - only Python vectors");
|
||||
return NULL;
|
||||
}
|
||||
if (self->cb_user) {
|
||||
|
|
|
@ -299,7 +299,7 @@ static void wm_window_match_keep_current_wm(const bContext *C,
|
|||
BKE_workspace_active_set(win->workspace_hook, workspace);
|
||||
win->scene = CTX_data_scene(C);
|
||||
|
||||
/* all windows get active screen from file */
|
||||
/* All windows get active screen from file. */
|
||||
if (screen->winid == 0) {
|
||||
WM_window_set_active_screen(win, workspace, screen);
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
|
|||
ot->srna, "filter_movie", (filter & FILE_TYPE_MOVIE) != 0, "Filter movie files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_python", (filter & FILE_TYPE_PYSCRIPT) != 0, "Filter python files", "");
|
||||
ot->srna, "filter_python", (filter & FILE_TYPE_PYSCRIPT) != 0, "Filter Python files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_boolean(
|
||||
ot->srna, "filter_font", (filter & FILE_TYPE_FTFONT) != 0, "Filter font files", "");
|
||||
|
|
Loading…
Reference in New Issue