From 1d82bd95e5f2259ab5fd9b0f2b1dc999e6556616 Mon Sep 17 00:00:00 2001 From: Variable <77773850+Variable-ind@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:29:55 +0500 Subject: [PATCH] Make clipping masks work with Move tool and offset image dialog (#1057) * made clipping masks work with offset image dialog * move tool now works with clipping masks --- src/Shaders/BlendLayers.gdshader | 10 ++- src/UI/Dialogs/ImageEffects/OffsetImage.gd | 79 ++++++++++++++++++++-- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/Shaders/BlendLayers.gdshader b/src/Shaders/BlendLayers.gdshader index 0a1d8a3d6..4aa0576f1 100644 --- a/src/Shaders/BlendLayers.gdshader +++ b/src/Shaders/BlendLayers.gdshader @@ -172,10 +172,18 @@ void fragment() { if (!origin_y_positive) { current_origin.y = -current_origin.y; } + // get origin of previous layer (used for clipping masks to work correctly) + vec2 prev_origin = texture(metadata, vec2(float(i - 1), 2.0 / metadata_size_float.y)).rg; + if (!origin_x_positive) { + prev_origin.x = -prev_origin.x; + } + if (!origin_y_positive) { + prev_origin.y = -prev_origin.y; + } float current_opacity = texture(metadata, vec2(layer_index, 1.0 / metadata_size_float.y)).r; vec2 uv = UV - current_origin; vec4 layer_color = texture(layers, vec3(uv, float(i))); - vec4 prev_layer_color = texture(layers, vec3(uv, float(i - 1))); + vec4 prev_layer_color = texture(layers, vec3(UV - prev_origin, float(i - 1))); float clipping_mask = texture(metadata, vec2(layer_index, 3.0 / metadata_size_float.y)).r; layer_color.a *= prev_layer_color.a * step(0.5, clipping_mask) + 1.0 * step(clipping_mask, 0.5); layer_color.a = border_trim(layer_color, uv); diff --git a/src/UI/Dialogs/ImageEffects/OffsetImage.gd b/src/UI/Dialogs/ImageEffects/OffsetImage.gd index acd3aeba9..eed79dd01 100644 --- a/src/UI/Dialogs/ImageEffects/OffsetImage.gd +++ b/src/UI/Dialogs/ImageEffects/OffsetImage.gd @@ -10,9 +10,6 @@ var wrap_around := false func _ready() -> void: super._ready() - var sm := ShaderMaterial.new() - sm.shader = shader - preview.set_material(sm) # Set in the order of the Animate enum animate_panel.add_float_property( "Offset X", $VBoxContainer/OffsetOptions/OffsetSliders.get_sliders()[0] @@ -39,17 +36,85 @@ func commit_action(cel: Image, project := Global.current_project) -> void: var params := {"offset": offset, "wrap_around": wrap_around, "selection": selection_tex} if !has_been_confirmed: - for param in params: - preview.material.set_shader_parameter(param, params[param]) + recalculate_preview(params) else: var gen := ShaderImageEffect.new() gen.generate_image(cel, shader, params, project.size) func _on_OffsetSliders_value_changed(_value: Vector2) -> void: - update_preview() + update_preview() # (from here, commit_action will be called, and then recalculate_preview) func _on_WrapCheckBox_toggled(button_pressed: bool) -> void: wrap_around = button_pressed - update_preview() + update_preview() # (from here, commit_action will be called, and then recalculate_preview) + + +func recalculate_preview(params: Dictionary) -> void: + var frame := Global.current_project.frames[_preview_idx] + commit_idx = _preview_idx + match affect: + SELECTED_CELS: + selected_cels.fill(Color(0, 0, 0, 0)) + blend_layers(selected_cels, frame, params, true) + preview_image.copy_from(selected_cels) + _: + current_frame.fill(Color(0, 0, 0, 0)) + blend_layers(current_frame, frame, params) + preview_image.copy_from(current_frame) + + +## Altered version of blend_layers() located in DrawingAlgos.gd +## This function is REQUIRED in order for offset effect to work correctly with cliping masks +func blend_layers( + image: Image, + frame: Frame, + effect_params := {}, + only_selected_cels := false, + only_selected_layers := false, +) -> void: + var project := Global.current_project + var frame_index := project.frames.find(frame) + var previous_ordered_layers: Array[int] = project.ordered_layers + project.order_layers(frame_index) + var textures: Array[Image] = [] + var gen := ShaderImageEffect.new() + # Nx4 texture, where N is the number of layers and the first row are the blend modes, + # the second are the opacities, the third are the origins and the fourth are the + # clipping mask booleans. + var metadata_image := Image.create(project.layers.size(), 4, false, Image.FORMAT_R8) + for i in project.layers.size(): + var ordered_index := project.ordered_layers[i] + var layer := project.layers[ordered_index] + var include := true if layer.is_visible_in_hierarchy() else false + if only_selected_cels and include: + var test_array := [frame_index, i] + if not test_array in project.selected_cels: + include = false + if only_selected_layers and include: + var layer_is_selected := false + for selected_cel in project.selected_cels: + if i == selected_cel[1]: + layer_is_selected = true + break + if not layer_is_selected: + include = false + var cel := frame.cels[ordered_index] + var cel_image := layer.display_effects(cel) + if include: ## apply offset effect to it + gen.generate_image(cel_image, shader, effect_params, project.size) + textures.append(cel_image) + DrawingAlgos.set_layer_metadata_image(layer, cel, metadata_image, ordered_index, include) + var texture_array := Texture2DArray.new() + texture_array.create_from_images(textures) + var params := { + "layers": texture_array, + "metadata": ImageTexture.create_from_image(metadata_image), + } + var blended := Image.create(project.size.x, project.size.y, false, image.get_format()) + var blend_layers_shader = DrawingAlgos.blend_layers_shader + gen.generate_image(blended, blend_layers_shader, params, project.size) + image.blend_rect(blended, Rect2i(Vector2i.ZERO, project.size), Vector2i.ZERO) + # Re-order the layers again to ensure correct canvas drawing + project.ordered_layers = previous_ordered_layers