diff --git a/Main.tscn b/Main.tscn index 2f13f5b61..0d3b4a01b 100644 --- a/Main.tscn +++ b/Main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=64 format=2] +[gd_scene load_steps=65 format=2] [ext_resource path="res://Themes & Styles/Dark Theme/Dark Theme.tres" type="Theme" id=1] [ext_resource path="res://Scripts/Main.gd" type="Script" id=2] @@ -53,6 +53,7 @@ [ext_resource path="res://Prefabs/EditPalettePopup.tscn" type="PackedScene" id=51] [ext_resource path="res://Prefabs/NewPaletteDialog.tscn" type="PackedScene" id=52] [ext_resource path="res://Prefabs/PaletteImportFileDialog.tscn" type="PackedScene" id=53] +[ext_resource path="res://Prefabs/Dialogs/RotateImage.tscn" type="PackedScene" id=54] [sub_resource type="InputEventKey" id=1] scancode = 88 @@ -1401,17 +1402,18 @@ columns = 5 [node name="SplashDialog" parent="." instance=ExtResource( 43 )] [node name="CreateNewImage" parent="." instance=ExtResource( 44 )] +window_title = "Confirmá, por favor..." [node name="OpenSprite" type="FileDialog" parent="."] margin_right = 515.0 margin_bottom = 348.0 -window_title = "Open a File" +window_title = "Abrir un Archivo" resizable = true mode = 0 access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +current_dir = "/home/azagaya/Documentos/VJ/Pixelorama" +current_path = "/home/azagaya/Documentos/VJ/Pixelorama/" [node name="SaveSprite" type="FileDialog" parent="."] anchor_left = 0.5 @@ -1426,12 +1428,17 @@ window_title = "Save Sprite as .pxo" resizable = true access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +current_dir = "/home/azagaya/Documentos/VJ/Pixelorama" +current_path = "/home/azagaya/Documentos/VJ/Pixelorama/" [node name="ImportSprites" parent="." instance=ExtResource( 45 )] +window_title = "Abrir Archivo(s)" +current_dir = "/home/azagaya/Documentos/VJ/Pixelorama" +current_path = "/home/azagaya/Documentos/VJ/Pixelorama/" [node name="ExportSprites" parent="." instance=ExtResource( 46 )] +current_dir = "/home/azagaya/Documentos/VJ/Pixelorama" +current_path = "/home/azagaya/Documentos/VJ/Pixelorama/" [node name="ScaleImage" parent="." instance=ExtResource( 47 )] @@ -1439,12 +1446,14 @@ current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρι [node name="OutlineDialog" parent="." instance=ExtResource( 49 )] visible = false +window_title = "Confirmá, por favor..." [node name="AboutDialog" parent="." instance=ExtResource( 50 )] [node name="QuitDialog" type="ConfirmationDialog" parent="."] margin_right = 200.0 margin_bottom = 70.0 +window_title = "Confirmá, por favor..." resizable = true dialog_text = "QUIT_LABEL" @@ -1460,15 +1469,19 @@ visible = false [node name="NewPaletteDialog" parent="." instance=ExtResource( 52 )] [node name="PaletteImportFileDialog" parent="." instance=ExtResource( 53 )] +window_title = "Abrir un Archivo" filters = PoolStringArray( "*.json ; JavaScript Object Notation", "*.gpl ; Gimp Palette Library" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +current_dir = "/home/azagaya/Documentos/VJ/Pixelorama" +current_path = "/home/azagaya/Documentos/VJ/Pixelorama/" [node name="LeftCursor" type="Sprite" parent="."] visible = false [node name="RightCursor" type="Sprite" parent="."] visible = false + +[node name="RotateImage" parent="." instance=ExtResource( 54 )] +visible = false [connection signal="pressed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorSwitchCenter/ColorSwitch" to="." method="_on_ColorSwitch_pressed"] [connection signal="color_changed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorPickersCenter/ColorPickersHorizontal/LeftColorPickerButton" to="." method="_on_LeftColorPickerButton_color_changed"] [connection signal="popup_closed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorPickersCenter/ColorPickersHorizontal/LeftColorPickerButton" to="." method="_can_draw_true"] diff --git a/Prefabs/Dialogs/RotateImage.gd b/Prefabs/Dialogs/RotateImage.gd new file mode 100644 index 000000000..506f3d680 --- /dev/null +++ b/Prefabs/Dialogs/RotateImage.gd @@ -0,0 +1,56 @@ +extends ConfirmationDialog + +var texture : ImageTexture +var aux_img : Image +var layer : Image + +func _ready(): + texture = ImageTexture.new() + texture.flags = 0 + aux_img = Image.new() + $VBoxContainer/HBoxContainer2/OptionButton.add_item("Rotxel") + $VBoxContainer/HBoxContainer2/OptionButton.add_item("Nearest neighbour") + +func set_sprite(sprite : Image): + aux_img.copy_from(sprite) + layer = sprite + texture.create_from_image(aux_img, 0) + $VBoxContainer/TextureRect.texture = texture + + +func _on_HSlider_value_changed(value): + rotate() + $VBoxContainer/HBoxContainer/SpinBox.value = $VBoxContainer/HBoxContainer/HSlider.value + + +func _on_SpinBox_value_changed(value): + $VBoxContainer/HBoxContainer/HSlider.value = $VBoxContainer/HBoxContainer/SpinBox.value + + +func _on_RotateImage_confirmed(): + Global.canvas.handle_undo("Draw") + match $VBoxContainer/HBoxContainer2/OptionButton.text: + "Rotxel": + Global.rotxel(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180) + "Nearest neighbour": + Global.nn_rotate(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180) + Global.canvas.handle_redo("Draw") + $VBoxContainer/HBoxContainer/HSlider.value = 0 + +func rotate(): + var sprite : Image = Image.new() + sprite.copy_from(aux_img) + match $VBoxContainer/HBoxContainer2/OptionButton.text: + "Rotxel": + Global.rotxel(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180) + "Nearest neighbour": + Global.nn_rotate(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180) + texture.create_from_image(sprite, 0) + + +func _on_OptionButton_item_selected(id): + rotate() + + +func _on_RotateImage_about_to_show(): + $VBoxContainer/HBoxContainer/HSlider.value = 0 diff --git a/Prefabs/Dialogs/RotateImage.tscn b/Prefabs/Dialogs/RotateImage.tscn new file mode 100644 index 000000000..12302e362 --- /dev/null +++ b/Prefabs/Dialogs/RotateImage.tscn @@ -0,0 +1,81 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Prefabs/Dialogs/RotateImage.gd" type="Script" id=1] + +[node name="RotateImage" type="ConfirmationDialog"] +margin_right = 245.0 +margin_bottom = 241.0 +window_title = "Confirmá, por favor..." +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 8.0 +margin_right = -8.0 +margin_bottom = -36.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TextureRect" type="TextureRect" parent="VBoxContainer"] +margin_right = 229.0 +margin_bottom = 145.0 +size_flags_vertical = 3 +expand = true +stretch_mode = 6 + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 149.0 +margin_right = 229.0 +margin_bottom = 169.0 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"] +margin_top = 3.0 +margin_right = 34.0 +margin_bottom = 17.0 +text = "Type:" + +[node name="OptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer2"] +margin_left = 38.0 +margin_right = 229.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 173.0 +margin_right = 229.0 +margin_bottom = 197.0 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"] +margin_top = 5.0 +margin_right = 44.0 +margin_bottom = 19.0 +text = "Angle: " + +[node name="HSlider" type="HSlider" parent="VBoxContainer/HBoxContainer"] +margin_left = 48.0 +margin_right = 151.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 359.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer"] +margin_left = 155.0 +margin_right = 229.0 +margin_bottom = 24.0 +max_value = 359.0 +[connection signal="about_to_show" from="." to="." method="_on_RotateImage_about_to_show"] +[connection signal="confirmed" from="." to="." method="_on_RotateImage_confirmed"] +[connection signal="item_selected" from="VBoxContainer/HBoxContainer2/OptionButton" to="." method="_on_OptionButton_item_selected"] +[connection signal="value_changed" from="VBoxContainer/HBoxContainer/HSlider" to="." method="_on_HSlider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpinBox" to="." method="_on_SpinBox_value_changed"] diff --git a/Scripts/Global.gd b/Scripts/Global.gd index d5768cafb..51e09bb14 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -612,6 +612,201 @@ func plot_circle(r : int) -> Array: err += x * 2 + 1 return circle_points +func scale3X(sprite : Image, tol : float = 50) -> Image: + var scaled = Image.new() + scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8) + scaled.lock() + sprite.lock() + 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 + + for x in range(1,sprite.get_width()-1): + for y in range(1,sprite.get_height()-1): + var xs : float = 3*x + var ys : float = 3*y + + a = sprite.get_pixel(x-1,y-1) + b = sprite.get_pixel(x,y-1) + c = sprite.get_pixel(x+1,y-1) + d = sprite.get_pixel(x-1,y) + e = sprite.get_pixel(x,y) + f = sprite.get_pixel(x+1,y) + g = sprite.get_pixel(x-1,y+1) + h = sprite.get_pixel(x,y+1) + i = sprite.get_pixel(x+1,y+1) + + var db : bool = similarColors(d, b, tol) + var dh : bool = similarColors(d, h, tol) + var bf : bool = similarColors(f, b, tol) + var ec : bool = similarColors(e, c, tol) + var ea : bool = similarColors(e, a, tol) + var fh : bool = similarColors(f, h, tol) + var eg : bool = similarColors(e, g, tol) + var ei : bool = similarColors(e, i, tol) + + scaled.set_pixel(xs-1, ys-1, d if (db and !dh and !bf) else e ) + scaled.set_pixel(xs, ys-1, b if (db and !dh and !bf and !ec) or + (bf and !db and !fh and !ea) else e) + scaled.set_pixel(xs+1, ys-1, f if (bf and !db and !fh) else e) + scaled.set_pixel(xs-1, ys, d if (dh and !fh and !db and !ea) or + (db and !dh and !bf and !eg) else e) + scaled.set_pixel(xs, ys, e); + scaled.set_pixel(xs+1, ys, f if (bf and !db and !fh and !ei) or + (fh and !bf and !dh and !ec) else e) + scaled.set_pixel(xs-1, ys+1, d if (dh and !fh and !db) else e) + scaled.set_pixel(xs, ys+1, h if (fh and !bf and !dh and !eg) or + (dh and !fh and !db and !ei) else e) + scaled.set_pixel(xs+1, ys+1, f if (fh and !bf and !dh) else e) + + scaled.unlock() + sprite.unlock() + return scaled + +func rotxel(sprite : Image, angle : float): + + # 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) + return + + var aux : Image = Image.new() + aux.copy_from(sprite) + var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/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 + 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 (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 + + 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); + + 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 nn_rotate(sprite : Image, angle : float): + var aux : Image = Image.new() + aux.copy_from(sprite) + sprite.lock() + aux.lock() + var ox: int + var oy: int + 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()): + 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() + +func similarColors(c1 : Color, c2 : Color, tol : float = 100) -> bool: + var dist = colorDistance(c1, c2) + return dist <= tol + +func colorDistance(c1 : Color, c2 : Color) -> float: + return sqrt(pow((c1.r - c2.r)*255, 2) + pow((c1.g - c2.g)*255, 2) + + pow((c1.b - c2.b)*255, 2) + pow((c1.a - c2.a)*255, 2)) + func _exit_tree() -> void: config_cache.set_value("window", "screen", OS.current_screen) config_cache.set_value("window", "maximized", OS.window_maximized || OS.window_fullscreen) diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 759dc7952..a1caf1f24 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -69,7 +69,8 @@ func _ready() -> void: "Flip Vertical" : KEY_MASK_SHIFT + KEY_V, "Invert colors" : 0, "Desaturation" : 0, - "Outline" : 0 + "Outline" : 0, + "Rotate Image" : 0 } var help_menu_items := { "View Splash Screen" : 0, @@ -346,6 +347,10 @@ func image_menu_id_pressed(id : int) -> void: Global.canvas.handle_redo("Draw") 6: # Outline $OutlineDialog.popup_centered() + 7: # Rotate + var image : Image = Global.canvas.layers[Global.canvas.current_layer_index][0] + $RotateImage.set_sprite(image) + $RotateImage.popup_centered() func help_menu_id_pressed(id : int) -> void: match id: