From e40d507a6ab76e56fafccc493926b3d62e46a329 Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas <35376950+OverloadedOrama@users.noreply.github.com> Date: Mon, 20 Jan 2025 01:08:31 +0200 Subject: [PATCH] Add a `transform` method to DrawingAlgos Can be used as a general method to apply rotation and skewing. It does not handle scaling though, and I am not sure if it should. --- src/Autoload/DrawingAlgos.gd | 38 +++++++++++++++ src/UI/Canvas/Selection.gd | 15 ++++-- src/UI/Dialogs/ImageEffects/RotateImage.gd | 51 ++++++-------------- src/UI/Dialogs/ImageEffects/RotateImage.tscn | 2 +- 4 files changed, 67 insertions(+), 39 deletions(-) diff --git a/src/Autoload/DrawingAlgos.gd b/src/Autoload/DrawingAlgos.gd index efc438f52..565ee775d 100644 --- a/src/Autoload/DrawingAlgos.gd +++ b/src/Autoload/DrawingAlgos.gd @@ -1,5 +1,6 @@ extends Node +enum RotationAlgorithm { ROTXEL_SMEAR, CLEANEDGE, OMNISCALE, NNS, NN, ROTXEL, URD } enum GradientDirection { TOP, BOTTOM, LEFT, RIGHT } ## Continuation from Image.Interpolation enum Interpolation { SCALE3X = 5, CLEANEDGE = 6, OMNISCALE = 7 } @@ -14,6 +15,8 @@ var omniscale_shader: Shader: if omniscale_shader == null: omniscale_shader = load("res://src/Shaders/Effects/Rotation/OmniScale.gdshader") return omniscale_shader +var rotxel_shader := preload("res://src/Shaders/Effects/Rotation/SmearRotxel.gdshader") +var nn_shader := preload("res://src/Shaders/Effects/Rotation/NearestNeighbour.gdshader") ## Blends canvas layers into passed image starting from the origin position @@ -270,6 +273,41 @@ func scale_3x(sprite: Image, tol := 0.196078) -> Image: return scaled +func transform( + image: Image, + params: Dictionary, + algorithm: RotationAlgorithm, + project := Global.current_project +) -> void: + var pivot: Vector2 = params.get("pivot", image.get_size() / 2) + if type_is_shader(algorithm): + params["pivot"] = pivot / Vector2(image.get_size()) + var shader := rotxel_shader + match algorithm: + RotationAlgorithm.CLEANEDGE: + shader = clean_edge_shader + RotationAlgorithm.OMNISCALE: + shader = omniscale_shader + RotationAlgorithm.NNS: + shader = nn_shader + var gen := ShaderImageEffect.new() + gen.generate_image(image, shader, params, project.size) + else: + var transformation_matrix: Transform2D = params.get("transformation_matrix", Transform2D()) + var angle := transformation_matrix.get_rotation() + match algorithm: + RotationAlgorithm.ROTXEL: + rotxel(image, angle, pivot) + RotationAlgorithm.NN: + nn_rotate(image, angle, pivot) + RotationAlgorithm.URD: + fake_rotsprite(image, angle, pivot) + + +func type_is_shader(algorithm: RotationAlgorithm) -> bool: + return algorithm <= RotationAlgorithm.NNS + + func rotxel(sprite: Image, angle: float, pivot: Vector2) -> void: if is_zero_approx(angle) or is_equal_approx(angle, TAU): return diff --git a/src/UI/Canvas/Selection.gd b/src/UI/Canvas/Selection.gd index 1df960651..e797d8860 100644 --- a/src/UI/Canvas/Selection.gd +++ b/src/UI/Canvas/Selection.gd @@ -46,6 +46,7 @@ var undo_data: Dictionary var gizmos: Array[Gizmo] = [] var dragged_gizmo: Gizmo = null var angle := 0.0 +var rotation_algorithm := DrawingAlgos.RotationAlgorithm.NN var content_pivot := Vector2.ZERO var mouse_pos_on_gizmo_drag := Vector2.ZERO var resize_keep_ratio := false @@ -405,7 +406,9 @@ func resize_selection() -> void: ) else: content_pivot = original_big_bounding_rectangle.size / 2.0 - DrawingAlgos.nn_rotate(preview_image, angle, content_pivot) + var transformation_matrix := Transform2D(angle, Vector2.ZERO) + var params := {"transformation_matrix": transformation_matrix, "pivot": content_pivot} + DrawingAlgos.transform(preview_image, params, rotation_algorithm) preview_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST) if temp_rect.size.x < 0: preview_image.flip_x() @@ -416,7 +419,9 @@ func resize_selection() -> void: Global.current_project.selection_map.copy_from(original_bitmap) var bitmap_pivot := original_big_bounding_rectangle.get_center() - DrawingAlgos.nn_rotate(Global.current_project.selection_map, angle, bitmap_pivot) + var bitmap_matrix := Transform2D(angle, Vector2.ZERO) + var bitmap_params := {"transformation_matrix": bitmap_matrix, "pivot": bitmap_pivot} + DrawingAlgos.transform(Global.current_project.selection_map, bitmap_params, rotation_algorithm) Global.current_project.selection_map.resize_bitmap_values( Global.current_project, size, temp_rect.size.x < 0, temp_rect.size.y < 0 ) @@ -552,7 +557,11 @@ func transform_content_confirm() -> void: src.crop(preview_image.get_width(), preview_image.get_height()) tilemap.apply_resizing_to_image(src, selected_cells, big_bounding_rectangle) else: - DrawingAlgos.nn_rotate(src, angle, content_pivot) + var transformation_matrix := Transform2D(angle, Vector2.ZERO) + var params := { + "transformation_matrix": transformation_matrix, "pivot": content_pivot + } + DrawingAlgos.transform(src, params, rotation_algorithm) src.resize( preview_image.get_width(), preview_image.get_height(), Image.INTERPOLATE_NEAREST ) diff --git a/src/UI/Dialogs/ImageEffects/RotateImage.gd b/src/UI/Dialogs/ImageEffects/RotateImage.gd index 9a5712e1d..31612ec49 100644 --- a/src/UI/Dialogs/ImageEffects/RotateImage.gd +++ b/src/UI/Dialogs/ImageEffects/RotateImage.gd @@ -80,7 +80,7 @@ func _calculate_pivot() -> void: func commit_action(cel: Image, project := Global.current_project) -> void: var angle := deg_to_rad(animate_panel.get_animated_value(commit_idx, Animate.ANGLE)) var init_angle := deg_to_rad(animate_panel.get_animated_value(commit_idx, Animate.INIT_ANGLE)) - + var rotation_algorithm := type_option_button.get_selected_id() var selection_tex: ImageTexture var image := Image.new() image.copy_from(cel) @@ -88,7 +88,7 @@ func commit_action(cel: Image, project := Global.current_project) -> void: var selection := project.selection_map.return_cropped_copy(project.size) selection_tex = ImageTexture.create_from_image(selection) - if !_type_is_shader(): + if not DrawingAlgos.type_is_shader(rotation_algorithm): var blank := project.new_empty_image() cel.blit_rect_mask( blank, selection, Rect2i(Vector2i.ZERO, cel.get_size()), Vector2i.ZERO @@ -97,41 +97,26 @@ func commit_action(cel: Image, project := Global.current_project) -> void: image.blit_rect_mask( blank, selection, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO ) - if _type_is_shader(): - var shader := rotxel_shader - var params := { - "transformation_matrix": Transform2D(angle, Vector2.ZERO), - "selection_tex": selection_tex, - "pivot": pivot / Vector2(cel.get_size()), - "preview": true - } - match type_option_button.get_selected_id(): - ROTXEL_SMEAR: - params["ending_angle"] = angle - params["initial_angle"] = init_angle - params["tolerance"] = tolerance_slider.value - CLEANEDGE: - shader = DrawingAlgos.clean_edge_shader - OMNISCALE: - shader = DrawingAlgos.omniscale_shader - NNS: - shader = nn_shader + var transformation_matrix := Transform2D(angle, Vector2.ZERO) + var params := { + "transformation_matrix": transformation_matrix, + "pivot": pivot, + "selection_tex": selection_tex, + "initial_angle": init_angle, + "ending_angle": angle, + "tolerance": tolerance_slider.value, + "preview": true + } + if DrawingAlgos.type_is_shader(rotation_algorithm): if !has_been_confirmed: + params["pivot"] /= Vector2(cel.get_size()) for param in params: preview.material.set_shader_parameter(param, params[param]) else: params["preview"] = false - var gen := ShaderImageEffect.new() - gen.generate_image(cel, shader, params, project.size) + DrawingAlgos.transform(cel, params, rotation_algorithm, project) else: - match type_option_button.get_selected_id(): - ROTXEL: - DrawingAlgos.rotxel(image, angle, pivot) - NN: - DrawingAlgos.nn_rotate(image, angle, pivot) - URD: - DrawingAlgos.fake_rotsprite(image, angle, pivot) - + DrawingAlgos.transform(image, params, rotation_algorithm, project) if project.has_selection and selection_checkbox.button_pressed: cel.blend_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO) else: @@ -140,10 +125,6 @@ func commit_action(cel: Image, project := Global.current_project) -> void: cel.convert_rgb_to_indexed() -func _type_is_shader() -> bool: - return type_option_button.get_selected_id() <= NNS - - func _on_TypeOptionButton_item_selected(_id: int) -> void: match type_option_button.get_selected_id(): ROTXEL_SMEAR: diff --git a/src/UI/Dialogs/ImageEffects/RotateImage.tscn b/src/UI/Dialogs/ImageEffects/RotateImage.tscn index ce55afc46..1e2477611 100644 --- a/src/UI/Dialogs/ImageEffects/RotateImage.tscn +++ b/src/UI/Dialogs/ImageEffects/RotateImage.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://src/UI/Dialogs/ImageEffects/RotateImage.gd" id="1"] [ext_resource type="PackedScene" uid="uid://bybqhhayl5ay5" path="res://src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn" id="2"] [ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/Sliders/ValueSlider.tscn" id="3"] -[ext_resource type="PackedScene" path="res://src/UI/Nodes/Sliders/ValueSliderV2.tscn" id="4"] +[ext_resource type="PackedScene" uid="uid://bbnqcxa20a5a5" path="res://src/UI/Nodes/Sliders/ValueSliderV2.tscn" id="4"] [node name="RotateImage" instance=ExtResource("2")] title = "Rotate Image"