mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-20 12:33:14 +00:00
Optimize layer blending modes and make them work on all GPUs (hopefully) - Fixes #938
This commit is contained in:
parent
88a2ef593e
commit
bc8a9de4db
14 changed files with 170 additions and 117 deletions
|
@ -9,64 +9,39 @@ var omniscale_shader := preload("res://src/Shaders/Effects/Rotation/OmniScale.gd
|
|||
|
||||
|
||||
## Blends canvas layers into passed image starting from the origin position
|
||||
func blend_all_layers(
|
||||
image: Image, frame: Frame, origin := Vector2i.ZERO, project := Global.current_project
|
||||
) -> void:
|
||||
var current_cels := frame.cels
|
||||
var textures: Array[Image] = []
|
||||
var opacities := PackedFloat32Array()
|
||||
var blend_modes := PackedInt32Array()
|
||||
|
||||
for i in Global.current_project.layers.size():
|
||||
if current_cels[i] is GroupCel:
|
||||
continue
|
||||
var layer := Global.current_project.layers[i]
|
||||
if not layer.is_visible_in_hierarchy():
|
||||
continue
|
||||
var cel_image := layer.display_effects(current_cels[i])
|
||||
textures.append(cel_image)
|
||||
opacities.append(current_cels[i].opacity)
|
||||
blend_modes.append(layer.blend_mode)
|
||||
var texture_array := Texture2DArray.new()
|
||||
texture_array.create_from_images(textures)
|
||||
var params := {
|
||||
"layers": texture_array,
|
||||
"opacities": opacities,
|
||||
"blend_modes": blend_modes,
|
||||
}
|
||||
var blended := Image.create(project.size.x, project.size.y, false, image.get_format())
|
||||
var gen := ShaderImageEffect.new()
|
||||
gen.generate_image(blended, blend_layers_shader, params, project.size)
|
||||
image.blend_rect(blended, Rect2i(Vector2i.ZERO, project.size), origin)
|
||||
|
||||
|
||||
## Blends selected cels of the given frame into passed image starting from the origin position
|
||||
func blend_selected_cels(
|
||||
image: Image, frame: Frame, origin := Vector2i.ZERO, project := Global.current_project
|
||||
func blend_layers(
|
||||
image: Image,
|
||||
frame: Frame,
|
||||
origin := Vector2i.ZERO,
|
||||
project := Global.current_project,
|
||||
only_selected := false
|
||||
) -> void:
|
||||
var textures: Array[Image] = []
|
||||
var opacities := PackedFloat32Array()
|
||||
var blend_modes := PackedInt32Array()
|
||||
for cel_ind in frame.cels.size():
|
||||
var test_array := [project.current_frame, cel_ind]
|
||||
if not test_array in project.selected_cels:
|
||||
continue
|
||||
if frame.cels[cel_ind] is GroupCel:
|
||||
continue
|
||||
var layer := project.layers[cel_ind]
|
||||
if not layer.is_visible_in_hierarchy():
|
||||
continue
|
||||
var cel := frame.cels[cel_ind]
|
||||
# Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
||||
# the second are the opacities and the third are the origins
|
||||
var metadata_image := Image.create(project.layers.size(), 3, false, Image.FORMAT_R8)
|
||||
for i in project.layers.size():
|
||||
var layer := project.layers[i]
|
||||
var include := true if layer.is_visible_in_hierarchy() else false
|
||||
if only_selected and include:
|
||||
var test_array := [project.frames.find(frame), i]
|
||||
if not test_array in project.selected_cels:
|
||||
include = false
|
||||
var cel := frame.cels[i]
|
||||
var cel_image := layer.display_effects(cel)
|
||||
textures.append(cel_image)
|
||||
opacities.append(cel.opacity)
|
||||
blend_modes.append(layer.blend_mode)
|
||||
# Store the blend mode
|
||||
metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
||||
# Store the opacity
|
||||
if include:
|
||||
metadata_image.set_pixel(i, 1, Color(cel.opacity, 0.0, 0.0, 0.0))
|
||||
else:
|
||||
metadata_image.set_pixel(i, 1, Color())
|
||||
var texture_array := Texture2DArray.new()
|
||||
texture_array.create_from_images(textures)
|
||||
var params := {
|
||||
"layers": texture_array,
|
||||
"opacities": opacities,
|
||||
"blend_modes": blend_modes,
|
||||
"metadata": ImageTexture.create_from_image(metadata_image),
|
||||
}
|
||||
var blended := Image.create(project.size.x, project.size.y, false, image.get_format())
|
||||
var gen := ShaderImageEffect.new()
|
||||
|
|
|
@ -484,9 +484,9 @@ func _blend_layers(
|
|||
image: Image, frame: Frame, origin := Vector2i.ZERO, project := Global.current_project
|
||||
) -> void:
|
||||
if export_layers == 0:
|
||||
DrawingAlgos.blend_all_layers(image, frame, origin, project)
|
||||
DrawingAlgos.blend_layers(image, frame, origin, project)
|
||||
elif export_layers == 1:
|
||||
DrawingAlgos.blend_selected_cels(image, frame, origin, project)
|
||||
DrawingAlgos.blend_layers(image, frame, origin, project, true)
|
||||
else:
|
||||
var layer := project.layers[export_layers - 2]
|
||||
var layer_image := Image.new()
|
||||
|
|
|
@ -196,10 +196,10 @@ func set_and_update_preview_image(frame_idx: int) -> void:
|
|||
var frame := Global.current_project.frames[frame_idx]
|
||||
selected_cels.resize(Global.current_project.size.x, Global.current_project.size.y)
|
||||
selected_cels.fill(Color(0, 0, 0, 0))
|
||||
DrawingAlgos.blend_selected_cels(selected_cels, frame)
|
||||
DrawingAlgos.blend_layers(selected_cels, frame, Vector2i.ZERO, Global.current_project, true)
|
||||
current_frame.resize(Global.current_project.size.x, Global.current_project.size.y)
|
||||
current_frame.fill(Color(0, 0, 0, 0))
|
||||
DrawingAlgos.blend_all_layers(current_frame, frame)
|
||||
DrawingAlgos.blend_layers(current_frame, frame)
|
||||
update_preview()
|
||||
|
||||
|
||||
|
|
|
@ -538,6 +538,7 @@ func change_cel(new_frame: int, new_layer := -1) -> void:
|
|||
if get_current_cel() is Cel3D:
|
||||
await RenderingServer.frame_post_draw
|
||||
await RenderingServer.frame_post_draw
|
||||
Global.canvas.update_all_layers = true
|
||||
Global.canvas.queue_redraw()
|
||||
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@ const float HCV_EPSILON = 1e-10;
|
|||
const float HSL_EPSILON = 1e-10;
|
||||
|
||||
uniform sampler2DArray layers : filter_nearest;
|
||||
uniform float[1024] opacities;
|
||||
uniform int[1024] blend_modes;
|
||||
uniform vec2[1024] origins;
|
||||
// Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
||||
// the second are the opacities and the third are the origins
|
||||
uniform sampler2D metadata : filter_nearest;
|
||||
uniform bool origin_x_positive = true;
|
||||
uniform bool origin_y_positive = true;
|
||||
|
||||
// Conversion functions from
|
||||
// https://gist.github.com/unitycoder/aaf94ddfe040ec2da93b58d3c65ab9d9
|
||||
|
@ -54,7 +56,7 @@ vec3 rgb_to_hsl(vec3 rgb)
|
|||
|
||||
vec4 blend(int blend_type, vec4 current_color, vec4 prev_color, float opacity) {
|
||||
vec4 result;
|
||||
if (current_color.a <= 0.01) {
|
||||
if (current_color.a <= 0.001 || opacity <= 0.001) {
|
||||
return prev_color;
|
||||
}
|
||||
current_color.rgb = current_color.rgb * opacity; // Premultiply with the layer texture's alpha to prevent semi transparent pixels from being too bright (ALL LAYER TYPES!)
|
||||
|
@ -142,15 +144,36 @@ float border_trim(vec4 color, vec2 uv) {
|
|||
|
||||
|
||||
void fragment() {
|
||||
vec4 col = texture(layers, vec3(UV - origins[0], 0.0));
|
||||
col.a = border_trim(col, UV - origins[0]);
|
||||
col.a *= opacities[0];
|
||||
ivec2 metadata_size = textureSize(metadata, 0) - 1;
|
||||
vec2 first_origin = texture(metadata, vec2(0.0, 2.0 / float(metadata_size.y))).rg;
|
||||
if (!origin_x_positive) {
|
||||
first_origin.x = -first_origin.x;
|
||||
}
|
||||
if (!origin_y_positive) {
|
||||
first_origin.y = -first_origin.y;
|
||||
}
|
||||
float first_opacity = texture(metadata, vec2(0.0, 1.0 / float(metadata_size.y))).r;
|
||||
vec4 col = texture(layers, vec3(UV - first_origin, 0.0));
|
||||
col.a = border_trim(col, UV - first_origin);
|
||||
col.a *= first_opacity;
|
||||
for(int i = 1; i < textureSize(layers, 0).z; i++) // Loops through every layer
|
||||
{
|
||||
vec2 uv = UV - origins[i];
|
||||
float blend_mode_float = texture(metadata, vec2(float(i) / float(metadata_size.x), 0.0)).r;
|
||||
// Blend modes are being stored as integers divided by 255, so convert them back to
|
||||
// their integer form
|
||||
int current_blend_mode = int(floor(blend_mode_float * 255.0));
|
||||
vec2 current_origin = texture(metadata, vec2(float(i) / float(metadata_size.x), 2.0 / float(metadata_size.y))).rg;
|
||||
if (!origin_x_positive) {
|
||||
current_origin.x = -current_origin.x;
|
||||
}
|
||||
if (!origin_y_positive) {
|
||||
current_origin.y = -current_origin.y;
|
||||
}
|
||||
float current_opacity = texture(metadata, vec2(float(i) / float(metadata_size.x), 1.0 / float(metadata_size.y))).r;
|
||||
vec2 uv = UV - current_origin;
|
||||
vec4 texture_color = texture(layers, vec3(uv, float(i)));
|
||||
texture_color.a = border_trim(texture_color, uv);
|
||||
col = blend(blend_modes[i], texture_color, col, opacities[i]);
|
||||
col = blend(current_blend_mode, texture_color, col, current_opacity);
|
||||
}
|
||||
COLOR = col;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ const CURSOR_SPEED_RATE := 6.0
|
|||
|
||||
var current_pixel := Vector2.ZERO
|
||||
var sprite_changed_this_frame := false ## For optimization purposes
|
||||
var update_all_layers := false
|
||||
var move_preview_location := Vector2i.ZERO
|
||||
var layer_texture_array := Texture2DArray.new()
|
||||
var layer_metadata_image := Image.new()
|
||||
var layer_metadata_texture := ImageTexture.new()
|
||||
|
||||
@onready var currently_visible_frame := $CurrentlyVisibleFrame as SubViewport
|
||||
@onready var current_frame_drawer := $CurrentlyVisibleFrame/CurrentFrameDrawer as Node2D
|
||||
|
@ -108,6 +112,19 @@ func update_texture(layer_i: int, frame_i := -1, project := Global.current_proje
|
|||
if frame_i < project.frames.size() and layer_i < project.layers.size():
|
||||
var current_cel := project.frames[frame_i].cels[layer_i]
|
||||
current_cel.update_texture()
|
||||
# Needed so that changes happening to the non-selected layer(s) are also visible
|
||||
# e.g. when undoing/redoing, when applying image effects to the entire frame, etc
|
||||
var layer := project.layers[layer_i]
|
||||
var cel_image: Image
|
||||
if Global.display_layer_effects:
|
||||
cel_image = layer.display_effects(current_cel)
|
||||
else:
|
||||
cel_image = current_cel.get_image()
|
||||
if (
|
||||
cel_image.get_size()
|
||||
== Vector2i(layer_texture_array.get_width(), layer_texture_array.get_height())
|
||||
):
|
||||
layer_texture_array.update_layer(cel_image, layer_i)
|
||||
|
||||
|
||||
func update_selected_cels_textures(project := Global.current_project) -> void:
|
||||
|
@ -120,36 +137,72 @@ func update_selected_cels_textures(project := Global.current_project) -> void:
|
|||
|
||||
|
||||
func draw_layers() -> void:
|
||||
var current_frame := Global.current_project.frames[Global.current_project.current_frame]
|
||||
var current_cels := current_frame.cels
|
||||
var textures: Array[Image] = []
|
||||
var opacities := PackedFloat32Array()
|
||||
var blend_modes := PackedInt32Array()
|
||||
var origins := PackedVector2Array()
|
||||
# Draw current frame layers
|
||||
for i in Global.current_project.layers.size():
|
||||
if current_cels[i] is GroupCel:
|
||||
continue
|
||||
var layer := Global.current_project.layers[i]
|
||||
if layer.is_visible_in_hierarchy():
|
||||
var project := Global.current_project
|
||||
var current_cels := project.frames[project.current_frame].cels
|
||||
var recreate_texture_array := (
|
||||
layer_texture_array.get_layers() != project.layers.size()
|
||||
or layer_texture_array.get_width() != project.size.x
|
||||
or layer_texture_array.get_height() != project.size.y
|
||||
)
|
||||
if recreate_texture_array:
|
||||
var textures: Array[Image] = []
|
||||
# Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
||||
# the second are the opacities and the third are the origins
|
||||
layer_metadata_image = Image.create(project.layers.size(), 3, false, Image.FORMAT_RG8)
|
||||
# Draw current frame layers
|
||||
for i in project.layers.size():
|
||||
var layer := project.layers[i]
|
||||
var cel_image: Image
|
||||
if Global.display_layer_effects:
|
||||
cel_image = layer.display_effects(current_cels[i])
|
||||
else:
|
||||
cel_image = current_cels[i].get_image()
|
||||
textures.append(cel_image)
|
||||
opacities.append(current_cels[i].opacity)
|
||||
if [Global.current_project.current_frame, i] in Global.current_project.selected_cels:
|
||||
origins.append(Vector2(move_preview_location) / Vector2(cel_image.get_size()))
|
||||
# Store the blend mode
|
||||
layer_metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
||||
# Store the opacity
|
||||
if layer.is_visible_in_hierarchy():
|
||||
layer_metadata_image.set_pixel(i, 1, Color(current_cels[i].opacity, 0.0, 0.0, 0.0))
|
||||
else:
|
||||
origins.append(Vector2.ZERO)
|
||||
blend_modes.append(layer.blend_mode)
|
||||
var texture_array := Texture2DArray.new()
|
||||
texture_array.create_from_images(textures)
|
||||
material.set_shader_parameter("layers", texture_array)
|
||||
material.set_shader_parameter("opacities", opacities)
|
||||
material.set_shader_parameter("blend_modes", blend_modes)
|
||||
material.set_shader_parameter("origins", origins)
|
||||
layer_metadata_image.set_pixel(i, 1, Color())
|
||||
# Store the origin
|
||||
if [project.current_frame, i] in project.selected_cels:
|
||||
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
|
||||
layer_metadata_image.set_pixel(i, 2, Color(origin.x, origin.y, 0.0, 0.0))
|
||||
else:
|
||||
layer_metadata_image.set_pixel(i, 2, Color())
|
||||
|
||||
layer_texture_array.create_from_images(textures)
|
||||
layer_metadata_texture.set_image(layer_metadata_image)
|
||||
else: # Update the TextureArray
|
||||
if layer_texture_array.get_layers() > 0:
|
||||
for i in project.layers.size():
|
||||
var layer := project.layers[i]
|
||||
var test_array := [project.current_frame, i]
|
||||
if not update_all_layers:
|
||||
if not test_array in project.selected_cels:
|
||||
continue
|
||||
var cel := current_cels[i]
|
||||
var cel_image: Image
|
||||
if Global.display_layer_effects:
|
||||
cel_image = layer.display_effects(cel)
|
||||
else:
|
||||
cel_image = cel.get_image()
|
||||
layer_texture_array.update_layer(cel_image, i)
|
||||
layer_metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
||||
if layer.is_visible_in_hierarchy():
|
||||
layer_metadata_image.set_pixel(i, 1, Color(cel.opacity, 0.0, 0.0, 0.0))
|
||||
else:
|
||||
layer_metadata_image.set_pixel(i, 1, Color())
|
||||
var origin := Vector2(move_preview_location).abs() / Vector2(cel_image.get_size())
|
||||
layer_metadata_image.set_pixel(i, 2, Color(origin.x, origin.y, 0.0, 0.0))
|
||||
layer_metadata_texture.update(layer_metadata_image)
|
||||
|
||||
material.set_shader_parameter("layers", layer_texture_array)
|
||||
material.set_shader_parameter("metadata", layer_metadata_texture)
|
||||
material.set_shader_parameter("origin_x_positive", move_preview_location.x > 0)
|
||||
material.set_shader_parameter("origin_y_positive", move_preview_location.y > 0)
|
||||
update_all_layers = false
|
||||
|
||||
|
||||
func refresh_onion() -> void:
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_6b0ox"]
|
||||
shader = ExtResource("1_253dh")
|
||||
shader_parameter/opacities = null
|
||||
shader_parameter/blend_modes = null
|
||||
shader_parameter/origins = null
|
||||
shader_parameter/origin_x_positive = true
|
||||
shader_parameter/origin_y_positive = true
|
||||
|
||||
[sub_resource type="CanvasItemMaterial" id="1"]
|
||||
blend_mode = 4
|
||||
|
|
|
@ -72,30 +72,33 @@ func _draw() -> void:
|
|||
|
||||
|
||||
func _draw_layers() -> void:
|
||||
var current_frame := Global.current_project.frames[frame_index]
|
||||
var project := Global.current_project
|
||||
var current_frame := project.frames[frame_index]
|
||||
var current_cels := current_frame.cels
|
||||
var textures: Array[Image] = []
|
||||
var opacities := PackedFloat32Array()
|
||||
var blend_modes := PackedInt32Array()
|
||||
# Nx3 texture, where N is the number of layers and the first row are the blend modes,
|
||||
# the second are the opacities and the third are the origins
|
||||
var metadata_image := Image.create(project.layers.size(), 3, false, Image.FORMAT_R8)
|
||||
# Draw current frame layers
|
||||
for i in Global.current_project.layers.size():
|
||||
for i in project.layers.size():
|
||||
if current_cels[i] is GroupCel:
|
||||
continue
|
||||
var layer := Global.current_project.layers[i]
|
||||
var layer := project.layers[i]
|
||||
var cel_image: Image
|
||||
if Global.display_layer_effects:
|
||||
cel_image = layer.display_effects(current_cels[i])
|
||||
else:
|
||||
cel_image = current_cels[i].get_image()
|
||||
textures.append(cel_image)
|
||||
metadata_image.set_pixel(i, 0, Color(layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
||||
if layer.is_visible_in_hierarchy():
|
||||
var cel_image: Image
|
||||
if Global.display_layer_effects:
|
||||
cel_image = layer.display_effects(current_cels[i])
|
||||
else:
|
||||
cel_image = current_cels[i].get_image()
|
||||
textures.append(cel_image)
|
||||
opacities.append(current_cels[i].opacity)
|
||||
blend_modes.append(layer.blend_mode)
|
||||
metadata_image.set_pixel(i, 1, Color(current_cels[i].opacity, 0.0, 0.0, 0.0))
|
||||
else:
|
||||
metadata_image.set_pixel(i, 1, Color(0.0, 0.0, 0.0, 0.0))
|
||||
var texture_array := Texture2DArray.new()
|
||||
texture_array.create_from_images(textures)
|
||||
material.set_shader_parameter("layers", texture_array)
|
||||
material.set_shader_parameter("opacities", opacities)
|
||||
material.set_shader_parameter("blend_modes", blend_modes)
|
||||
material.set_shader_parameter("metadata", ImageTexture.create_from_image(metadata_image))
|
||||
|
||||
|
||||
func _on_AnimationTimer_timeout() -> void:
|
||||
|
|
|
@ -878,7 +878,9 @@ func clear_selection(use_undo := false) -> void:
|
|||
func _get_preview_image() -> void:
|
||||
var project := Global.current_project
|
||||
var blended_image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
||||
DrawingAlgos.blend_selected_cels(blended_image, project.frames[project.current_frame])
|
||||
DrawingAlgos.blend_layers(
|
||||
blended_image, project.frames[project.current_frame], Vector2i.ZERO, project, true
|
||||
)
|
||||
if original_preview_image.is_empty():
|
||||
original_preview_image = Image.create(
|
||||
big_bounding_rectangle.size.x, big_bounding_rectangle.size.y, false, Image.FORMAT_RGBA8
|
||||
|
|
|
@ -10,7 +10,7 @@ var params := {}
|
|||
func _about_to_popup() -> void:
|
||||
Global.canvas.selection.transform_content_confirm()
|
||||
var frame := Global.current_project.frames[Global.current_project.current_frame]
|
||||
DrawingAlgos.blend_selected_cels(selected_cels, frame)
|
||||
DrawingAlgos.blend_layers(selected_cels, frame, Vector2i.ZERO, Global.current_project, true)
|
||||
|
||||
preview_image.copy_from(selected_cels)
|
||||
preview.texture = ImageTexture.create_from_image(preview_image)
|
||||
|
|
|
@ -125,7 +125,7 @@ func change_mask():
|
|||
var tiles := Global.current_project.tiles
|
||||
var tiles_size := tiles.tile_size
|
||||
var image := Image.create(tiles_size.x, tiles_size.y, false, Image.FORMAT_RGBA8)
|
||||
DrawingAlgos.blend_all_layers(image, current_frame)
|
||||
DrawingAlgos.blend_layers(image, current_frame)
|
||||
if (
|
||||
image.get_used_rect().size == Vector2i.ZERO
|
||||
or not $VBoxContainer/HBoxContainer/Masking.button_pressed
|
||||
|
|
|
@ -74,7 +74,7 @@ func capture_frame() -> void:
|
|||
else:
|
||||
var frame := project.frames[project.current_frame]
|
||||
image = Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
||||
DrawingAlgos.blend_all_layers(image, frame, Vector2i.ZERO, project)
|
||||
DrawingAlgos.blend_layers(image, frame, Vector2i.ZERO, project)
|
||||
|
||||
if mode == Mode.CANVAS:
|
||||
if resize != 100:
|
||||
|
|
|
@ -917,20 +917,16 @@ func _on_MergeDownLayer_pressed() -> void:
|
|||
var top_image := top_layer.display_effects(top_cel)
|
||||
var bottom_cel := frame.cels[bottom_layer.index]
|
||||
var textures: Array[Image] = []
|
||||
var opacities := PackedFloat32Array()
|
||||
var blend_modes := PackedInt32Array()
|
||||
var metadata_image := Image.create(2, 3, false, Image.FORMAT_R8)
|
||||
textures.append(bottom_cel.get_image())
|
||||
opacities.append(bottom_cel.opacity)
|
||||
blend_modes.append(bottom_layer.blend_mode)
|
||||
metadata_image.set_pixel(0, 1, Color(1.0, 0.0, 0.0, 0.0))
|
||||
textures.append(top_image)
|
||||
opacities.append(frame.cels[top_layer.index].opacity)
|
||||
blend_modes.append(top_layer.blend_mode)
|
||||
metadata_image.set_pixel(1, 0, Color(top_layer.blend_mode / 255.0, 0.0, 0.0, 0.0))
|
||||
metadata_image.set_pixel(1, 1, Color(frame.cels[top_layer.index].opacity, 0.0, 0.0, 0.0))
|
||||
var texture_array := Texture2DArray.new()
|
||||
texture_array.create_from_images(textures)
|
||||
var params := {
|
||||
"layers": texture_array,
|
||||
"opacities": opacities,
|
||||
"blend_modes": blend_modes,
|
||||
"layers": texture_array, "metadata": ImageTexture.create_from_image(metadata_image)
|
||||
}
|
||||
var bottom_image := Image.create(
|
||||
top_image.get_width(), top_image.get_height(), false, top_image.get_format()
|
||||
|
|
|
@ -155,6 +155,7 @@ func _on_ExpandButton_pressed() -> void:
|
|||
func _on_VisibilityButton_pressed() -> void:
|
||||
Global.canvas.selection.transform_content_confirm()
|
||||
Global.current_project.layers[layer].visible = !Global.current_project.layers[layer].visible
|
||||
Global.canvas.update_all_layers = true
|
||||
Global.canvas.queue_redraw()
|
||||
if Global.select_layer_on_button_click:
|
||||
_select_current_layer()
|
||||
|
|
Loading…
Add table
Reference in a new issue