Bootstrap Chameleon Logo

Use Images and Image Brush

Creating SImage

Editor Style

、 To use an existing image in EditorStyle, you can specify it with "Brush". For example:

"SImage": {
    "Image": {
        "Style": "FEditorStyle",
        "Brush": "AutomationTools.TestAutomation"
    }
}

NOTE
EditorStyles supported in TAPython include: "FEditorStyle", "FCoreStyle", and "FAppStyle events".

ImagePath

The "ImagePath" field can be used to specify the image path. This path is relative to the current UI JSON file of the tool.

For example:

An SImage assigned with the field 'ImagePath'

"SImage": {
    "Aka": "AUniqueName",
    "ColorAndOpacity": [1, 1, 1, 1],
    "ImagePath": "../ChameleonGallery/Images/cartoon_game_map.png",
    "ToolTipText": "'ImagePath' is a relative path with the local ui json file.",
    "OnMouseButtonDown": "print('clicked')"
}

For images used in the current tool, it is convenient to place them in the tool's directory or its subdirectories.

ImagePathInPlugin

To use an image located in the plugin directory, you can specify it with "ImagePathInPlugin". The path is also relative, relative to the TAPython directory. For example:

"ImagePathInPlugin": "Resources/Icon128.png",the actual path is <Your_Project>\Plugins\TAPython\Resources/Icon128.png.

An SImage assigned with the field 'ImagePathInPlugin'

"SImage": {
    "Aka": "AUniqueName",
    "ImagePathInPlugin": "Resources/Icon128.png",
    "DesiredSizeOverride": [64, 64]
}

CAUTION
When specifying image content in SImage through "ImagePath" and "ImagePathInPlugin", you also need to specify the "Aka" widget name.

SetImageFromPath

unreal.ChameleonData.set_image_from_path(aka_name, image_file_path, brush_width, brush_height),can be used to set the image content of SImage from the image path.

  • aka_name: The name of the SImage widget.
  • image_file_path: The absolute path of the image. (If a relative path is provided, it is relative to the Resource directory in the Plugin directory)
  • brush_width: The width of the generated Brush
  • brush_height: The height of the generated Brush

CAUTION
Using relative paths in image_file_path increases the memory overhead for developers. It is recommended to use absolute paths when using set_image_from_path.

Fill SImage

In addition to the content introduced below, more details on filling and modifying SImage can be found here: Image and Render Target

set_image_pixel

chameleon_instance.set_image_pixels(aka_name, pixel_colors, width, height), can be used to fill the image content of SImage with pixels.

chameleon_instance:An instance of ChameleonData.

For example, the following code sets the image content of SImage to a 64x64 random black and white noise map.

def set_random_image_data(self):
    width = 64
    height = 64
    colors = [unreal.LinearColor(1, 1, 1, 1)  if random.randint(0, 1) else unreal.LinearColor(0, 0, 0, 1) for _ in range(width * height)]
    self.data.set_image_pixels(self.ui_image, colors, width, height)

set_image_data

Filling SImage with pixels is very intuitive, but in practice, we often use "raw_data" to fill the content of SImage. Its execution speed is relatively faster.

chameleon_instance.set_image_data(aka_name, raw_data, width, height, channel_num=4, bgr=True)

  • raw_data: The flatten raw data of image, len(RawDataIn) == Height * Width * ChannelNum

For example, in TAPython_Taichi_StableFluid_UE5, numpy is used as the data structure for images. We can fill the image into SImage with the following code.

import numpy as np
...
self.image_im = np.ones((self.height, self.width, 4), dtype=np.uint8) * 255
...
self.data.set_image_data(self.ui_image, self.image_im.data.tobytes(), width=self.width, height=self.height)

Another example: We got a base64 encoded string of an image from the network. We can fill it into SImage with the following code.

from PIL import Image

def on_timer(self, count):
    if count != 0 and count != -1:
        progress_json = self.api.get_progress()
        progress = progress_json.get("progress", None)
        current_image = progress_json.get("current_image", None)
        if progress:
            self._set_progress(progress)
        if current_image:
            img = Image.open(io.BytesIO(base64.b64decode(current_image)))
            self.data.set_image_data(self.ui_image, img.tobytes(), img.width, img.height, channel_num=3, bgr=False)

A stable-diffusion Unreal Editor snapshot

TIP
Third-party libraries like numpy and PIL can greatly speed up image calculations. GPU calculations can be considered with Taichi. Additionally, modifying images efficiently can be achieved through UE's own RenderTarget and Material.

Attentive developers may have noticed that TAPython does not provide a so-called get_image_data method for SImage. This is intentional because the SImages operated in TAPython are created by ourselves and the content is filled by us. Therefore, we also have a copy of the image content data in the Python end. Thus, "avoid repeatedly obtaining Image content from UI widgets and directly obtain it from Python data" is a more reasonable approach.

Event

The SImage widget supports the following three mouse events, making it easy for users to interact with SImage in the interface.

  • OnMouseMove
  • OnMouseEnter
  • OnMouseLeave events

In the following example, the variable placeholders %uv and %mouse_flags will be replaced by actual uv values and mouse button flags at runtime.

{
    "SImage": {
        "DesiredSizeOverride": [200, 200],
        "Aka": "ImageCanvas",
        "OnMouseLeave": "your_tool.on_mouse_leave(%mouse_flags)",
        "OnMouseMove": "your_tool.on_mouse_move(%uv, %mouse_flags)"
    }
}
  • %uv: tuple, (u, v)
  • %mouse_flags: Flags. The 0th, 1st, and 2nd bits represent the left, middle, and right mouse button states, respectively. The values are bitwise OR'ed. For example, if the left and right mouse buttons are pressed at the same time, the value is 5 (1 + 4).
def on_mouse_move(self, uv, mouse_flags):
    self.cursor_pos_x, self.cursor_pos_y = uv
    self.bLMB = (mouse_flags % 2)
    self.bMMB = (mouse_flags>>1 % 2)
    self.bRMB = (mouse_flags>>2 % 2)

Painter on a slate removing the coverage of the Background Image

For example, we captured the user's mouse input in Image, and then passed it to StableFluid for calculation. For more details, see: Modify SImage content and Set Pixels to RenderTarget in Unreal Engine. The sample repository is at TAPython_Taichi_StableFluid_UE5.

GIF showing modification of volumetric clouds using only Python through TAPython plugin and Taichi-lang

References

PythonTextureLib