1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-30 23:19:49 +00:00

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.
This commit is contained in:
Emmanouil Papadeas 2025-01-20 01:08:31 +02:00
parent 0809dc2dcf
commit e40d507a6a
4 changed files with 67 additions and 39 deletions

View file

@ -1,5 +1,6 @@
extends Node extends Node
enum RotationAlgorithm { ROTXEL_SMEAR, CLEANEDGE, OMNISCALE, NNS, NN, ROTXEL, URD }
enum GradientDirection { TOP, BOTTOM, LEFT, RIGHT } enum GradientDirection { TOP, BOTTOM, LEFT, RIGHT }
## Continuation from Image.Interpolation ## Continuation from Image.Interpolation
enum Interpolation { SCALE3X = 5, CLEANEDGE = 6, OMNISCALE = 7 } enum Interpolation { SCALE3X = 5, CLEANEDGE = 6, OMNISCALE = 7 }
@ -14,6 +15,8 @@ var omniscale_shader: Shader:
if omniscale_shader == null: if omniscale_shader == null:
omniscale_shader = load("res://src/Shaders/Effects/Rotation/OmniScale.gdshader") omniscale_shader = load("res://src/Shaders/Effects/Rotation/OmniScale.gdshader")
return omniscale_shader 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 ## 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 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: func rotxel(sprite: Image, angle: float, pivot: Vector2) -> void:
if is_zero_approx(angle) or is_equal_approx(angle, TAU): if is_zero_approx(angle) or is_equal_approx(angle, TAU):
return return

View file

@ -46,6 +46,7 @@ var undo_data: Dictionary
var gizmos: Array[Gizmo] = [] var gizmos: Array[Gizmo] = []
var dragged_gizmo: Gizmo = null var dragged_gizmo: Gizmo = null
var angle := 0.0 var angle := 0.0
var rotation_algorithm := DrawingAlgos.RotationAlgorithm.NN
var content_pivot := Vector2.ZERO var content_pivot := Vector2.ZERO
var mouse_pos_on_gizmo_drag := Vector2.ZERO var mouse_pos_on_gizmo_drag := Vector2.ZERO
var resize_keep_ratio := false var resize_keep_ratio := false
@ -405,7 +406,9 @@ func resize_selection() -> void:
) )
else: else:
content_pivot = original_big_bounding_rectangle.size / 2.0 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) preview_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
if temp_rect.size.x < 0: if temp_rect.size.x < 0:
preview_image.flip_x() preview_image.flip_x()
@ -416,7 +419,9 @@ func resize_selection() -> void:
Global.current_project.selection_map.copy_from(original_bitmap) Global.current_project.selection_map.copy_from(original_bitmap)
var bitmap_pivot := original_big_bounding_rectangle.get_center() 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.selection_map.resize_bitmap_values(
Global.current_project, size, temp_rect.size.x < 0, temp_rect.size.y < 0 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()) src.crop(preview_image.get_width(), preview_image.get_height())
tilemap.apply_resizing_to_image(src, selected_cells, big_bounding_rectangle) tilemap.apply_resizing_to_image(src, selected_cells, big_bounding_rectangle)
else: 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( src.resize(
preview_image.get_width(), preview_image.get_height(), Image.INTERPOLATE_NEAREST preview_image.get_width(), preview_image.get_height(), Image.INTERPOLATE_NEAREST
) )

View file

@ -80,7 +80,7 @@ func _calculate_pivot() -> void:
func commit_action(cel: Image, project := Global.current_project) -> 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 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 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 selection_tex: ImageTexture
var image := Image.new() var image := Image.new()
image.copy_from(cel) 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) var selection := project.selection_map.return_cropped_copy(project.size)
selection_tex = ImageTexture.create_from_image(selection) 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() var blank := project.new_empty_image()
cel.blit_rect_mask( cel.blit_rect_mask(
blank, selection, Rect2i(Vector2i.ZERO, cel.get_size()), Vector2i.ZERO 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( image.blit_rect_mask(
blank, selection, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO blank, selection, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO
) )
if _type_is_shader(): var transformation_matrix := Transform2D(angle, Vector2.ZERO)
var shader := rotxel_shader var params := {
var params := { "transformation_matrix": transformation_matrix,
"transformation_matrix": Transform2D(angle, Vector2.ZERO), "pivot": pivot,
"selection_tex": selection_tex, "selection_tex": selection_tex,
"pivot": pivot / Vector2(cel.get_size()), "initial_angle": init_angle,
"preview": true "ending_angle": angle,
} "tolerance": tolerance_slider.value,
match type_option_button.get_selected_id(): "preview": true
ROTXEL_SMEAR: }
params["ending_angle"] = angle if DrawingAlgos.type_is_shader(rotation_algorithm):
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
if !has_been_confirmed: if !has_been_confirmed:
params["pivot"] /= Vector2(cel.get_size())
for param in params: for param in params:
preview.material.set_shader_parameter(param, params[param]) preview.material.set_shader_parameter(param, params[param])
else: else:
params["preview"] = false params["preview"] = false
var gen := ShaderImageEffect.new() DrawingAlgos.transform(cel, params, rotation_algorithm, project)
gen.generate_image(cel, shader, params, project.size)
else: else:
match type_option_button.get_selected_id(): DrawingAlgos.transform(image, params, rotation_algorithm, project)
ROTXEL:
DrawingAlgos.rotxel(image, angle, pivot)
NN:
DrawingAlgos.nn_rotate(image, angle, pivot)
URD:
DrawingAlgos.fake_rotsprite(image, angle, pivot)
if project.has_selection and selection_checkbox.button_pressed: if project.has_selection and selection_checkbox.button_pressed:
cel.blend_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO) cel.blend_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
else: else:
@ -140,10 +125,6 @@ func commit_action(cel: Image, project := Global.current_project) -> void:
cel.convert_rgb_to_indexed() 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: func _on_TypeOptionButton_item_selected(_id: int) -> void:
match type_option_button.get_selected_id(): match type_option_button.get_selected_id():
ROTXEL_SMEAR: ROTXEL_SMEAR:

View file

@ -3,7 +3,7 @@
[ext_resource type="Script" path="res://src/UI/Dialogs/ImageEffects/RotateImage.gd" id="1"] [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://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" 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")] [node name="RotateImage" instance=ExtResource("2")]
title = "Rotate Image" title = "Rotate Image"