diff --git a/src/Autoload/DrawingAlgos.gd b/src/Autoload/DrawingAlgos.gd index 64710ae2c..b8740bcd5 100644 --- a/src/Autoload/DrawingAlgos.gd +++ b/src/Autoload/DrawingAlgos.gd @@ -62,146 +62,163 @@ func scale3X(sprite : Image, tol : float = 50) -> Image: return scaled -func rotxel(sprite : Image, angle : float) -> void: +func rotxel(sprite : Image, angle : float, pixels : Array) -> void: # If angle is simple, then nn rotation is the best - if angle == 0 || angle == PI/2 || angle == PI || angle == 2*PI: - nn_rotate(sprite, angle) + nn_rotate(sprite, angle, pixels) return var aux : Image = Image.new() aux.copy_from(sprite) -# warning-ignore:integer_division -# warning-ignore:integer_division - var center : Vector2 = Vector2(sprite.get_width() / 2, sprite.get_height() / 2) + var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1) + var center : Vector2 = selection_rectangle.position + ((selection_rectangle.end - selection_rectangle.position) / 2) var ox : int var oy : int var p : Color aux.lock() sprite.lock() - for x in range(sprite.get_width()): - for y in range(sprite.get_height()): - var dx = 3*(x - center.x) - var dy = 3*(y - center.y) - var found_pixel : bool = false - for k in range(9): - var i = -1 + k % 3 + for pix in pixels: + var x = pix.x + var y = pix.y + var dx = 3*(x - center.x) + var dy = 3*(y - center.y) + var found_pixel : bool = false + for k in range(9): + var i = -1 + k % 3 # warning-ignore:integer_division - var j = -1 + int(k / 3) - var dir = atan2(dy + j, dx + i) - var mag = sqrt(pow(dx + i, 2) + pow(dy + j, 2)) - dir -= angle - ox = round(center.x*3 + 1 + mag*cos(dir)) - oy = round(center.y*3 + 1 + mag*sin(dir)) + var j = -1 + int(k / 3) + var dir = atan2(dy + j, dx + i) + var mag = sqrt(pow(dx + i, 2) + pow(dy + j, 2)) + dir -= angle + ox = round(center.x*3 + 1 + mag*cos(dir)) + oy = round(center.y*3 + 1 + mag*sin(dir)) - if (sprite.get_width() % 2 != 0): - ox += 1 - oy += 1 + if (sprite.get_width() % 2 != 0): + ox += 1 + oy += 1 - if (ox >= 0 && ox < sprite.get_width()*3 - && oy >= 0 && oy < sprite.get_height()*3): - found_pixel = true - break + if (ox >= 0 && ox < sprite.get_width()*3 + && oy >= 0 && oy < sprite.get_height()*3): + found_pixel = true + break - if !found_pixel: - sprite.set_pixel(x, y, Color(0,0,0,0)) - continue + if !found_pixel: + sprite.set_pixel(x, y, Color(0,0,0,0)) + continue - var fil : int = oy % 3 - var col : int = ox % 3 - var index : int = col + 3*fil + var fil : int = oy % 3 + var col : int = ox % 3 + var index : int = col + 3*fil - ox = round((ox - 1)/3.0); - oy = round((oy - 1)/3.0); - var a : Color - var b : Color - var c : Color - var d : Color - var e : Color - var f : Color - var g : Color - var h : Color - var i : Color - if (ox == 0 || ox == sprite.get_width() - 1 || - oy == 0 || oy == sprite.get_height() - 1): - p = aux.get_pixel(ox, oy) - else: - a = aux.get_pixel(ox-1,oy-1); - b = aux.get_pixel(ox,oy-1); - c = aux.get_pixel(ox+1,oy-1); - d = aux.get_pixel(ox-1,oy); - e = aux.get_pixel(ox,oy); - f = aux.get_pixel(ox+1,oy); - g = aux.get_pixel(ox-1,oy+1); - h = aux.get_pixel(ox,oy+1); - i = aux.get_pixel(ox+1,oy+1); + ox = round((ox - 1)/3.0); + oy = round((oy - 1)/3.0); + var a : Color + var b : Color + var c : Color + var d : Color + var e : Color + var f : Color + var g : Color + var h : Color + var i : Color + if (ox == 0 || ox == sprite.get_width() - 1 || + oy == 0 || oy == sprite.get_height() - 1): + p = aux.get_pixel(ox, oy) + else: + a = aux.get_pixel(ox-1,oy-1); + b = aux.get_pixel(ox,oy-1); + c = aux.get_pixel(ox+1,oy-1); + d = aux.get_pixel(ox-1,oy); + e = aux.get_pixel(ox,oy); + f = aux.get_pixel(ox+1,oy); + g = aux.get_pixel(ox-1,oy+1); + h = aux.get_pixel(ox,oy+1); + i = aux.get_pixel(ox+1,oy+1); - match(index): - 0: - p = d if (similarColors(d,b) && !similarColors(d,h) - && !similarColors(b,f)) else e; - 1: - p = b if ((similarColors(d,b) && !similarColors(d,h) && - !similarColors(b,f) && !similarColors(e,c)) || - (similarColors(b,f) && !similarColors(d,b) && - !similarColors(f,h) && !similarColors(e,a))) else e; - 2: - p = f if (similarColors(b,f) && !similarColors(d,b) && - !similarColors(f,h)) else e; - 3: - p = d if ((similarColors(d,h) && !similarColors(f,h) && - !similarColors(d,b) && !similarColors(e,a)) || - (similarColors(d,b) && !similarColors(d,h) && - !similarColors(b,f) && !similarColors(e,g))) else e; - 4: - p = e - 5: - p = f if((similarColors(b,f) && !similarColors(d,b) && - !similarColors(f,h) && !similarColors(e,i)) - || (similarColors(f,h) && !similarColors(b,f) && - !similarColors(d,h) && !similarColors(e,c))) else e; - 6: - p = d if (similarColors(d,h) && !similarColors(f,h) && - !similarColors(d,b)) else e; - 7: - p = h if ((similarColors(f,h) && !similarColors(f,b) && - !similarColors(d,h) && !similarColors(e,g)) - || (similarColors(d,h) && !similarColors(f,h) && - !similarColors(d,b) && !similarColors(e,i))) else e; - 8: - p = f if (similarColors(f,h) && !similarColors(f,b) && - !similarColors(d,h)) else e; - sprite.set_pixel(x, y, p) + match(index): + 0: + p = d if (similarColors(d,b) && !similarColors(d,h) + && !similarColors(b,f)) else e; + 1: + p = b if ((similarColors(d,b) && !similarColors(d,h) && + !similarColors(b,f) && !similarColors(e,c)) || + (similarColors(b,f) && !similarColors(d,b) && + !similarColors(f,h) && !similarColors(e,a))) else e; + 2: + p = f if (similarColors(b,f) && !similarColors(d,b) && + !similarColors(f,h)) else e; + 3: + p = d if ((similarColors(d,h) && !similarColors(f,h) && + !similarColors(d,b) && !similarColors(e,a)) || + (similarColors(d,b) && !similarColors(d,h) && + !similarColors(b,f) && !similarColors(e,g))) else e; + 4: + p = e + 5: + p = f if((similarColors(b,f) && !similarColors(d,b) && + !similarColors(f,h) && !similarColors(e,i)) + || (similarColors(f,h) && !similarColors(b,f) && + !similarColors(d,h) && !similarColors(e,c))) else e; + 6: + p = d if (similarColors(d,h) && !similarColors(f,h) && + !similarColors(d,b)) else e; + 7: + p = h if ((similarColors(f,h) && !similarColors(f,b) && + !similarColors(d,h) && !similarColors(e,g)) + || (similarColors(d,h) && !similarColors(f,h) && + !similarColors(d,b) && !similarColors(e,i))) else e; + 8: + p = f if (similarColors(f,h) && !similarColors(f,b) && + !similarColors(d,h)) else e; + sprite.set_pixel(x, y, p) sprite.unlock() aux.unlock() -func fake_rotsprite(sprite : Image, angle : float) -> void: - sprite.copy_from(scale3X(sprite)) - nn_rotate(sprite,angle) +func fake_rotsprite(sprite : Image, angle : float, pixels : Array) -> void: + var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1) + var selected_sprite := Image.new() + selected_sprite = sprite.get_rect(selection_rectangle) + selected_sprite.copy_from(scale3X(selected_sprite)) + nn_rotate(selected_sprite, angle, []) # warning-ignore:integer_division # warning-ignore:integer_division - sprite.resize(sprite.get_width() / 3, sprite.get_height() / 3, 0) + selected_sprite.resize(selected_sprite.get_width() / 3, selected_sprite.get_height() / 3, 0) + sprite.blit_rect(selected_sprite, Rect2(Vector2.ZERO, selected_sprite.get_size()), selection_rectangle.position) -func nn_rotate(sprite : Image, angle : float) -> void: +func nn_rotate(sprite : Image, angle : float, pixels : Array) -> void: var aux : Image = Image.new() aux.copy_from(sprite) sprite.lock() aux.lock() var ox: int var oy: int -# warning-ignore:integer_division -# warning-ignore:integer_division - var center : Vector2 = Vector2(sprite.get_width() / 2, sprite.get_height() / 2) - for x in range(sprite.get_width()): - for y in range(sprite.get_height()): + var center : Vector2 + if pixels: + var selection_rectangle := Rect2(pixels[0].x, pixels[0].y, pixels[-1].x - pixels[0].x + 1, pixels[-1].y - pixels[0].y + 1) + center = selection_rectangle.position + ((selection_rectangle.end - selection_rectangle.position) / 2) + for pix in pixels: + var x = pix.x + var y = pix.y ox = (x - center.x)*cos(angle) + (y - center.y)*sin(angle) + center.x oy = -(x - center.x)*sin(angle) + (y - center.y)*cos(angle) + center.y if ox >= 0 && ox < sprite.get_width() && oy >= 0 && oy < sprite.get_height(): sprite.set_pixel(x, y, aux.get_pixel(ox, oy)) else: sprite.set_pixel(x, y, Color(0,0,0,0)) + else: +# warning-ignore:integer_division +# warning-ignore:integer_division + center = Vector2(sprite.get_width() / 2, sprite.get_height() / 2) + for x in range(sprite.get_width()): + for y in range(sprite.get_height()): + ox = (x - center.x)*cos(angle) + (y - center.y)*sin(angle) + center.x + oy = -(x - center.x)*sin(angle) + (y - center.y)*cos(angle) + center.y + if ox >= 0 && ox < sprite.get_width() && oy >= 0 && oy < sprite.get_height(): + sprite.set_pixel(x, y, aux.get_pixel(ox, oy)) + else: + sprite.set_pixel(x, y, Color(0,0,0,0)) sprite.unlock() aux.unlock() diff --git a/src/UI/Dialogs/ImageEffects/RotateImage.gd b/src/UI/Dialogs/ImageEffects/RotateImage.gd index 6a3cb733b..8aa5dcd95 100644 --- a/src/UI/Dialogs/ImageEffects/RotateImage.gd +++ b/src/UI/Dialogs/ImageEffects/RotateImage.gd @@ -14,6 +14,8 @@ func _ready() -> void: func set_nodes() -> void: preview = $VBoxContainer/Preview + selection_checkbox = $VBoxContainer/OptionsContainer/SelectionCheckBox + affect_option_button = $VBoxContainer/OptionsContainer/AffectOptionButton func _about_to_show() -> void: @@ -22,13 +24,14 @@ func _about_to_show() -> void: func commit_action(_cel : Image, _pixels : Array, _project : Project = Global.current_project) -> void: + var angle : float = deg2rad(angle_hslider.value) match type_option_button.text: "Rotxel": - DrawingAlgos.rotxel(_cel,angle_hslider.value*PI/180) + DrawingAlgos.rotxel(_cel, angle, _pixels) "Nearest neighbour": - DrawingAlgos.nn_rotate(_cel,angle_hslider.value*PI/180) + DrawingAlgos.nn_rotate(_cel, angle, _pixels) "Upscale, Rotate and Downscale": - DrawingAlgos.fake_rotsprite(_cel,angle_hslider.value*PI/180) + DrawingAlgos.fake_rotsprite(_cel, angle, _pixels) func _confirmed() -> void: diff --git a/src/UI/Dialogs/ImageEffects/RotateImage.tscn b/src/UI/Dialogs/ImageEffects/RotateImage.tscn index 029745f39..015751498 100644 --- a/src/UI/Dialogs/ImageEffects/RotateImage.tscn +++ b/src/UI/Dialogs/ImageEffects/RotateImage.tscn @@ -25,8 +25,8 @@ __meta__ = { } [node name="Preview" type="TextureRect" parent="VBoxContainer"] -margin_left = 14.0 -margin_right = 214.0 +margin_left = 31.0 +margin_right = 231.0 margin_bottom = 200.0 rect_min_size = Vector2( 200, 200 ) size_flags_horizontal = 4 @@ -39,7 +39,7 @@ show_behind_parent = true [node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] margin_top = 204.0 -margin_right = 229.0 +margin_right = 263.0 margin_bottom = 224.0 [node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"] @@ -50,7 +50,7 @@ text = "Type:" [node name="TypeOptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer2"] margin_left = 38.0 -margin_right = 229.0 +margin_right = 263.0 margin_bottom = 20.0 mouse_default_cursor_shape = 2 size_flags_horizontal = 3 @@ -58,7 +58,7 @@ size_flags_vertical = 3 [node name="AngleOptions" type="HBoxContainer" parent="VBoxContainer"] margin_top = 228.0 -margin_right = 229.0 +margin_right = 263.0 margin_bottom = 252.0 [node name="Label" type="Label" parent="VBoxContainer/AngleOptions"] @@ -69,7 +69,7 @@ text = "Angle:" [node name="AngleHSlider" type="HSlider" parent="VBoxContainer/AngleOptions"] margin_left = 44.0 -margin_right = 151.0 +margin_right = 185.0 margin_bottom = 24.0 mouse_default_cursor_shape = 2 size_flags_horizontal = 3 @@ -80,12 +80,33 @@ __meta__ = { } [node name="AngleSpinBox" type="SpinBox" parent="VBoxContainer/AngleOptions"] -margin_left = 155.0 -margin_right = 229.0 +margin_left = 189.0 +margin_right = 263.0 margin_bottom = 24.0 mouse_default_cursor_shape = 2 max_value = 359.0 suffix = "°" + +[node name="OptionsContainer" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 256.0 +margin_right = 263.0 +margin_bottom = 280.0 + +[node name="SelectionCheckBox" type="CheckBox" parent="VBoxContainer/OptionsContainer"] +margin_right = 160.0 +margin_bottom = 24.0 +mouse_default_cursor_shape = 2 +pressed = true +text = "Only affect selection" + +[node name="AffectOptionButton" type="OptionButton" parent="VBoxContainer/OptionsContainer"] +margin_left = 164.0 +margin_right = 263.0 +margin_bottom = 24.0 +mouse_default_cursor_shape = 2 +text = "Current cel" +items = [ "Current cel", null, false, 0, null, "Current frame", null, false, 1, null, "All frames", null, false, 2, null, "All projects", null, false, 3, null ] +selected = 0 [connection signal="item_selected" from="VBoxContainer/HBoxContainer2/TypeOptionButton" to="." method="_on_TypeOptionButton_item_selected"] [connection signal="value_changed" from="VBoxContainer/AngleOptions/AngleHSlider" to="." method="_on_HSlider_value_changed"] [connection signal="value_changed" from="VBoxContainer/AngleOptions/AngleSpinBox" to="." method="_on_SpinBox_value_changed"]