# SPDX-License-Identifier: GPL-2.0-or-later # __all__ = ( "load_image", ) # limited replacement for BPyImage.comprehensiveImageLoad def load_image( imagepath, dirname="", place_holder=False, recursive=False, ncase_cmp=True, convert_callback=None, verbose=False, relpath=None, check_existing=False, force_reload=False, ): """ Return an image from the file path with options to search multiple paths and return a placeholder if its not found. :arg filepath: The image filename If a path precedes it, this will be searched as well. :type filepath: string :arg dirname: is the directory where the image may be located - any file at the end will be ignored. :type dirname: string :arg place_holder: if True a new place holder image will be created. this is useful so later you can relink the image to its original data. :type place_holder: bool :arg recursive: If True, directories will be recursively searched. Be careful with this if you have files in your root directory because it may take a long time. :type recursive: bool :arg ncase_cmp: on non windows systems, find the correct case for the file. :type ncase_cmp: bool :arg convert_callback: a function that takes an existing path and returns a new one. Use this when loading image formats blender may not support, the CONVERT_CALLBACK can take the path for a GIF (for example), convert it to a PNG and return the PNG's path. For formats blender can read, simply return the path that is given. :type convert_callback: function :arg relpath: If not None, make the file relative to this path. :type relpath: None or string :arg check_existing: If true, returns already loaded image datablock if possible (based on file path). :type check_existing: bool :arg force_reload: If true, force reloading of image (only useful when `check_existing` is also enabled). :type force_reload: bool :return: an image or None :rtype: :class:`bpy.types.Image` """ import os import bpy # ------------------------------------------------------------------------- # Utility Functions def _image_load_placeholder(path): name = path if type(path) is str: name = name.encode("utf-8", "replace") name = name.decode("utf-8", "replace") name = os.path.basename(name) image = bpy.data.images.new(name, 128, 128) # allow the path to be resolved later image.filepath = path image.source = 'FILE' return image def _image_load(path): import bpy if convert_callback: path = convert_callback(path) # Ensure we're not relying on the 'CWD' to resolve the path. if not os.path.isabs(path): path = os.path.abspath(path) try: image = bpy.data.images.load(path, check_existing=check_existing) except RuntimeError: image = None if verbose: if image: print(" image loaded '%s'" % path) else: print(" image load failed '%s'" % path) # image path has been checked so the path could not be read for some # reason, so be sure to return a placeholder if place_holder and image is None: image = _image_load_placeholder(path) if image: if force_reload: image.reload() if relpath is not None: # make relative from bpy.path import relpath as relpath_fn # can't always find the relative path # (between drive letters on windows) try: filepath_rel = relpath_fn(path, start=relpath) except ValueError: filepath_rel = None if filepath_rel is not None: image.filepath_raw = filepath_rel return image def _recursive_search(paths, filename_check): for path in paths: for dirpath, _dirnames, filenames in os.walk(path): # skip '.svn' if dirpath[0] in {".", b'.'}: continue for filename in filenames: if filename_check(filename): yield os.path.join(dirpath, filename) # ------------------------------------------------------------------------- imagepath = bpy.path.native_pathsep(imagepath) if verbose: print("load_image('%s', '%s', ...)" % (imagepath, dirname)) if os.path.exists(imagepath): return _image_load(imagepath) variants = [imagepath] if dirname: variants += [ os.path.join(dirname, imagepath), os.path.join(dirname, bpy.path.basename(imagepath)), ] for filepath_test in variants: if ncase_cmp: ncase_variants = ( filepath_test, bpy.path.resolve_ncase(filepath_test), ) else: ncase_variants = (filepath_test, ) for nfilepath in ncase_variants: if os.path.exists(nfilepath): return _image_load(nfilepath) if recursive: search_paths = [] for dirpath_test in (os.path.dirname(imagepath), dirname): if os.path.exists(dirpath_test): search_paths.append(dirpath_test) search_paths[:] = bpy.path.reduce_dirs(search_paths) imagepath_base = bpy.path.basename(imagepath) if ncase_cmp: imagepath_base = imagepath_base.lower() def image_filter(fn): return (imagepath_base == fn.lower()) else: def image_filter(fn): return (imagepath_base == fn) nfilepath = next(_recursive_search(search_paths, image_filter), None) if nfilepath is not None: return _image_load(nfilepath) # None of the paths exist so return placeholder if place_holder: return _image_load_placeholder(imagepath) # TODO comprehensiveImageLoad also searched in bpy.config.textureDir return None