def split_normal(self, image: bpy.types.Image): roughness_name = self.new_texture_name_with_suffix( image.name, 'roughness', 'tga') if image.get('normalmap_converted', None): return image, bpy.data.images.get(roughness_name, None) if bpy.app.version > (2, 83, 0): buffer = np.zeros(image.size[0] * image.size[1] * 4, np.float32) image.pixels.foreach_get(buffer) else: buffer = np.array(image.pixels[:]) mask = buffer[2::4] roughness_rgb = np.dstack((mask, mask, mask, np.ones_like(mask))) roughness_texture = Source2ShaderBase.make_texture( roughness_name, image.size, roughness_rgb, True) buffer[1::4] = np.subtract(1, buffer[1::4]) buffer[2::4] = 1.0 if bpy.app.version > (2, 83, 0): image.pixels.foreach_set(buffer.tolist()) else: image.pixels[:] = buffer.tolist() image.pack() image['normalmap_converted'] = True return image, roughness_texture
def _encode_temp_image(tmp_image: bpy.types.Image, file_format: str) -> bytes: with tempfile.TemporaryDirectory() as tmpdirname: tmpfilename = tmpdirname + '/img' tmp_image.filepath_raw = tmpfilename tmp_image.file_format = file_format tmp_image.save() with open(tmpfilename, "rb") as f: return f.read()
def cache_image_file(image: bpy.types.Image, cache_check=True): image_path = Path(image.filepath_from_user()) if not image.packed_file and image.source != 'GENERATED': if not image_path.is_file(): log.warn("Image is missing", image, image_path) return None image_suffix = Path(image.filepath).suffix.lower() if image_suffix in SUPPORTED_FORMATS and f".{image.file_format.lower()}" in SUPPORTED_FORMATS: return image_path if image_suffix in READONLY_IMAGE_FORMATS: return image_path old_filepath = image.filepath_raw old_file_format = image.file_format temp_path = get_temp_file(DEFAULT_FORMAT, image.name) if cache_check and image.source != 'GENERATED' and temp_path.is_file(): return temp_path image_source = image.source image.filepath_raw = str(temp_path) image.file_format = BLENDER_DEFAULT_FORMAT try: image.save() finally: image.filepath_raw = old_filepath image.file_format = old_file_format image.source = image_source return temp_path
def _encode_temp_image(tmp_image: bpy.types.Image, file_format: str) -> bytes: with tempfile.TemporaryDirectory() as tmpdirname: tmpfilename = tmpdirname + '/img' tmp_image.filepath_raw = tmpfilename # NOT A TYPO!!! If you delete this line, the # assignment on the next line will not work. tmp_image.file_format tmp_image.file_format = file_format tmp_image.save() with open(tmpfilename, "rb") as f: return f.read()
def convert_normalmap(image: bpy.types.Image): if image.get('normalmap_converted', None): return image if bpy.app.version > (2, 83, 0): buffer = np.zeros(image.size[0] * image.size[1] * 4, np.float32) image.pixels.foreach_get(buffer) else: buffer = np.array(image.pixels[:]) buffer[1::4] = np.subtract(1, buffer[1::4]) if bpy.app.version > (2, 83, 0): image.pixels.foreach_set(buffer.tolist()) else: image.pixels[:] = buffer.tolist() image.pack() image['normalmap_converted'] = True return image
def tmp_encode_movie(image: bpy.types.Image): """ Reads the image bytes from disk back. """ path = image.filepath_from_user() with open(path, "rb") as f: encoded_image = f.read() return encoded_image
def cache_image_file(image: bpy.types.Image, depsgraph) -> str: """ See if image is a file, cache image pixels to temporary folder if not. Return image file path. """ if image.source != 'FILE': temp_path = _get_temp_image_path(image) if image.is_dirty or not os.path.isfile(temp_path): image.save_render(temp_path) return temp_path file_path = image.filepath_from_user() if file_path.lower().endswith('.ies'): if os.path.isfile(file_path): return file_path if not image.packed_file: log.warn("Can't load image", image, file_path) return None temp_path = _get_temp_image_path(image, "ies") if not os.path.isfile(temp_path): # save data of packed file Path(temp_path).write_bytes(image.packed_file.data) return temp_path if image.is_dirty or not os.path.isfile(file_path) \ or file_path.lower().endswith(UNSUPPORTED_IMAGES): target_format, target_extension = IMAGE_FORMATS.get( image.file_format, DEFAULT_FORMAT) # getting file path from image cache and if such file not exist saving image to cache temp_path = _get_temp_image_path(image, target_extension) if image.is_dirty or not os.path.isfile(temp_path): _save_temp_image(image, target_format, temp_path, depsgraph) return temp_path return file_path
def _make_temp_image_copy(guard: TmpImageGuard, src_image: bpy.types.Image): """Makes a temporary copy of src_image. Will be cleaned up with guard.""" guard.image = src_image.copy() tmp_image = guard.image tmp_image.update() if src_image.is_dirty: # Unsaved changes aren't copied by .copy(), so do them ourselves tmp_buf = np.empty(src_image.size[0] * src_image.size[1] * 4, np.float32) src_image.pixels.foreach_get(tmp_buf) tmp_image.pixels.foreach_set(tmp_buf)
def convert_ssbump(image: bpy.types.Image): if image.get('ssbump_converted', None): return image if bpy.app.version > (2, 83, 0): buffer = np.zeros(image.size[0] * image.size[1] * 4, np.float32) image.pixels.foreach_get(buffer) else: buffer = np.array(image.pixels[:]) buffer[0::4] *= 0.5 buffer[0::4] += 0.33 buffer[1::4] *= 0.5 buffer[1::4] += 0.33 buffer[2::4] *= 0.2 buffer[2::4] += 0.8 if bpy.app.version > (2, 83, 0): image.pixels.foreach_set(buffer.tolist()) else: image.pixels[:] = buffer.tolist() image.pack() image['ssbump_converted'] = True return image
def save_image_targetdir(arg_image: bpy.types.Image, arg_directory: str, arg_colormode: str = 'RGBA', arg_colordepth: str = '8', arg_compression: int = 15) -> bool: """指定ディレクトリにテクスチャをPNG形式で保存する Args: arg_image (bpy.types.Image): 保存テクスチャ arg_directory (str): 指定ディレクトリ arg_colormode (str, optional): カラーモード指定. Defaults to 'RGBA'. arg_colordepth (str, optional): 色深度指定. Defaults to '8'. arg_compression (int, optional): 圧縮率指定. Defaults to 15. Returns: bool: 実行正否 """ # 保存ファイルパスを作成する savepath = arg_directory + "\\" + arg_image.name + ".png" # 保存ファイルパスを指定する arg_image.filepath_raw = savepath # ファイルフォーマットをPNGに設定する arg_image.file_format = 'PNG' # 参照パスを保存するため、一度保存を行う arg_image.save() # レンダー色空間で保存するためのシーンを取得する # (https://docs.blender.org/api/current/bpy.types.Scene.html) render_scene = bpy.context.scene # 現在のカラーマネジメントのビュー変換を取得する current_view_transform = render_scene.view_settings.view_transform # カラーマネジメントのビュー変換を[標準]に設定する(Filmicだと灰色に出力されるため) # (https://docs.blender.org/api/current/bpy.types.ColorManagedViewSettings.html) render_scene.view_settings.view_transform = 'Standard' # シーンのレンダリング設定からイメージフォーマット設定の参照を取得する scene_imagesettings = render_scene.render.image_settings # カラーフォーマットを設定する scene_imagesettings.color_mode = arg_colormode # 色深度を設定する scene_imagesettings.color_depth = arg_colordepth # 圧縮率を設定する scene_imagesettings.compression = arg_compression # シーンのレンダリング設定を利用して画像を保存する arg_image.save_render(filepath=savepath, scene=render_scene) # 変更したビュー変換を元に戻す render_scene.view_settings.view_transform = current_view_transform return True
def cache_image_file(image: bpy.types.Image, cache_check=True): image_path = Path(image.filepath_from_user()) if not image.packed_file and image.source != 'GENERATED': if not image_path.is_file(): log.warn("Image is missing", image, image_path) return None image_suffix = image_path.suffix.lower() if image_suffix in SUPPORTED_FORMATS and\ f".{image.file_format.lower()}" in SUPPORTED_FORMATS and not image.is_dirty: return image_path if image_suffix in READONLY_IMAGE_FORMATS: return image_path temp_path = get_temp_file(DEFAULT_FORMAT, image_path.stem) if cache_check and image.source != 'GENERATED' and temp_path.is_file(): return temp_path scene = bpy.context.scene user_format = scene.render.image_settings.file_format user_color_mode = scene.render.image_settings.color_mode # in some scenes the color_mode is undefined # we can read it but unable to assign back, so switch it to 'RGB' if color_mode isn't selected if not user_color_mode: user_color_mode = 'RGB' scene.render.image_settings.file_format = BLENDER_DEFAULT_FORMAT scene.render.image_settings.color_mode = BLENDER_DEFAULT_COLOR_MODE try: image.save_render(filepath=str(temp_path)) finally: scene.render.image_settings.file_format = user_format scene.render.image_settings.color_mode = user_color_mode return temp_path
def _make_temp_image_copy(guard: TmpImageGuard, src_image: bpy.types.Image): """Makes a temporary copy of src_image. Will be cleaned up with guard.""" guard.image = src_image.copy() tmp_image = guard.image tmp_image.update() # See #1564 and T95616 tmp_image.scale(*src_image.size) if src_image.is_dirty: # Warning, img size change doesn't make it dirty, see T95616 # Unsaved changes aren't copied by .copy(), so do them ourselves tmp_buf = np.empty(src_image.size[0] * src_image.size[1] * 4, np.float32) src_image.pixels.foreach_get(tmp_buf) tmp_image.pixels.foreach_set(tmp_buf)
def __encode_from_image(self, image: bpy.types.Image) -> bytes: # See if there is an existing file we can use. data = None if image.source == 'FILE' and image.file_format == self.file_format and \ not image.is_dirty: if image.packed_file is not None: data = image.packed_file.data else: src_path = bpy.path.abspath(image.filepath_raw) if os.path.isfile(src_path): with open(src_path, 'rb') as f: data = f.read() # Check magic number is right if data: if self.file_format == 'PNG': if data.startswith(b'\x89PNG'): return data elif self.file_format == 'JPEG': if data.startswith(b'\xff\xd8\xff'): return data # Copy to a temp image and save. tmp_image = None try: tmp_image = image.copy() tmp_image.update() if image.is_dirty: # Copy the pixels to get the changes tmp_buf = np.empty(image.size[0] * image.size[1] * 4, np.float32) image.pixels.foreach_get(tmp_buf) tmp_image.pixels.foreach_set(tmp_buf) tmp_buf = None # GC this return _encode_temp_image(tmp_image, self.file_format) finally: if tmp_image is not None: bpy.data.images.remove(tmp_image, do_unlink=True)
def __encode_from_image(self, image: bpy.types.Image) -> bytes: # See if there is an existing file we can use. if image.source == 'FILE' and image.file_format == self.file_format and \ not image.is_dirty: if image.packed_file is not None: return image.packed_file.data else: src_path = bpy.path.abspath(image.filepath_raw) if os.path.isfile(src_path): with open(src_path, 'rb') as f: return f.read() # Copy to a temp image and save. tmp_image = None try: tmp_image = image.copy() if image.is_dirty: tmp_image.pixels = image.pixels[:] return _encode_temp_image(tmp_image, self.file_format) finally: if tmp_image is not None: bpy.data.images.remove(tmp_image, do_unlink=True)
def load_texture(texture: bpy.types.Image) -> int: """Load the texture, return OpenGL error code.""" return texture.gl_load(filter=bgl.GL_NEAREST, mag=bgl.GL_NEAREST)
def sync(rpr_context, image: bpy.types.Image, use_color_space=None, frame_number=None): """ Creates pyrpr.Image from bpy.types.Image """ from rprblender.engine.export_engine import ExportEngine color_space = image.colorspace_settings.name if use_color_space: color_space = use_color_space if image.source == 'SEQUENCE': image_key = key(image, color_space, frame_number=frame_number) else: if image.size[0] * image.size[1] * image.channels == 0: log.warn("Image has no data", image) return None image_key = key(image, color_space) if image_key in rpr_context.images: return rpr_context.images[image_key] log("sync", image) if image.source == 'TILED': # UDIM Tiles rpr_image = rpr_context.create_tiled_image(key=image_key) if rpr_image: # if context doesn't support tiles - export as regular image udim_path_split = image.filepath_from_user().split('.') for tile in image.tiles: tile_path = '.'.join(udim_path_split[:-2] + [tile.label] + udim_path_split[-1:]) tile_key = key(image, color_space, UDIM_tile=tile.label) tile_image = rpr_context.create_image_file(tile_key, tile_path) set_image_gamma(tile_image, image, color_space) tile_image.set_name(str(tile_key)) rpr_image.set_udim_tile(tile.number, tile_image) rpr_image.set_name(str(image_key)) return rpr_image pixels = image.pixels if image.source == 'SEQUENCE': file_path = get_sequence_frame_file_path(image.filepath_from_user(), frame_number) if not file_path: return None rpr_image = rpr_context.create_image_file(image_key, file_path) elif rpr_context.engine_type != ExportEngine.TYPE and hasattr( pixels, 'foreach_get'): data = utils.get_prop_array_data(pixels) data = np.flipud( data.reshape(image.size[1], image.size[0], image.channels)) rpr_image = rpr_context.create_image_data(image_key, np.ascontiguousarray(data)) elif image.source in ('FILE', 'GENERATED'): file_path = cache_image_file(image, rpr_context.blender_data['depsgraph']) rpr_image = rpr_context.create_image_file(image_key, file_path) else: # loading image by pixels data = utils.get_prop_array_data(pixels) data = np.flipud( data.reshape(image.size[1], image.size[0], image.channels)) rpr_image = rpr_context.create_image_data(image_key, np.ascontiguousarray(data)) rpr_image.set_name(str(image_key)) set_image_gamma(rpr_image, image, color_space) return rpr_image
def sync(rpr_context, image: bpy.types.Image, use_color_space=None, frame_number=None): """ Creates pyrpr.Image from bpy.types.Image """ from rprblender.engine.export_engine import ExportEngine color_space = image.colorspace_settings.name if use_color_space: color_space = use_color_space if image.source == 'SEQUENCE': image_key = key(image, color_space, frame_number) else: if image.size[0] * image.size[1] * image.channels == 0: log.warn("Image has no data", image) return None image_key = key(image, color_space) if image_key in rpr_context.images: return rpr_context.images[image_key] log("sync", image) pixels = image.pixels if image.source == 'SEQUENCE': file_path = get_sequence_frame_file_path(image.filepath_from_user(), frame_number) if not file_path: return None rpr_image = rpr_context.create_image_file(image_key, file_path) elif rpr_context.engine_type != ExportEngine.TYPE and hasattr( pixels, 'foreach_get'): data = utils.get_prop_array_data(pixels) data = np.flipud( data.reshape(image.size[1], image.size[0], image.channels)) rpr_image = rpr_context.create_image_data(image_key, np.ascontiguousarray(data)) elif image.source in ('FILE', 'GENERATED'): file_path = cache_image_file(image, rpr_context.blender_data['depsgraph']) rpr_image = rpr_context.create_image_file(image_key, file_path) else: # loading image by pixels data = utils.get_prop_array_data(pixels) data = np.flipud( data.reshape(image.size[1], image.size[0], image.channels)) rpr_image = rpr_context.create_image_data(image_key, np.ascontiguousarray(data)) rpr_image.set_name(str(image_key)) # TODO: implement more correct support of image color space types # RPRImageTexture node color space names are in caps, unlike in Blender if color_space in ('sRGB', 'BD16', 'Filmic Log', 'SRGB'): rpr_image.set_gamma(2.2) elif color_space not in ('Non-Color', 'Raw', 'Linear', 'LINEAR'): log.warn("Ignoring unsupported image color space type", color_space, image) return rpr_image
def load_texture(texture: bpy.types.Image) -> int: """Load the texture, return OpenGL error code.""" return texture.gl_load()
def draw_image( # noqa: WPS210, WPS211 x: float, # noqa: WPS111 y: float, # noqa: WPS111 width: float, height: float, image: bpy.types.Image, transparency: float, crop: Color = (0, 0, 1, 1), ) -> None: """Draw a image on the screen. Parameters: x: Bottom-left x-coordinate y: Bottom-left y-coordinate width: Width of the image height: Height of the image image: Image to be drawn transparency: Image transparency crop: Tuple describing how the image should be cropped Raises: Exception: Failed to load into an OpenGL texture """ coords = [ (x, y), (x + width, y), (x, y + height), (x + width, y + height), ] uvs = [ (crop[0], crop[1]), (crop[2], crop[1]), (crop[0], crop[3]), (crop[2], crop[3]), ] indices = [(0, 1, 2), (2, 1, 3)] shader = gpu.shader.from_builtin('2D_IMAGE') batch = batch_for_shader(shader, 'TRIS', { 'pos': coords, 'texCoord': uvs }, indices=indices) # send image to gpu if it isn't there already if image.gl_load(): raise Exception() # texture identifier on gpu texture_id = image.bindcode # in case someone disabled it before bgl.glEnable(bgl.GL_BLEND) # bind texture to image unit 0 bgl.glActiveTexture(bgl.GL_TEXTURE0) bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture_id) shader.bind() # tell shader to use the image that is bound to image unit 0 shader.uniform_int('image', 0) batch.draw(shader) bgl.glDisable(bgl.GL_TEXTURE_2D)
def _has_missing_reference(image: bpy.types.Image): if not image.packed_file: path = image.filepath_from_user() return not os.path.exists(path) return False