From ea97ec85f8536aec2fb42ac0afdfc151aa6177d5 Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Tue, 24 Dec 2019 16:48:07 +0200 Subject: [PATCH] Added outline generation, under new "Image" menu You can currently change outline color & thickness --- Main.tscn | 63 +++++++++++++++++++++-- Scripts/Global.gd | 2 + Scripts/Main.gd | 124 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 153 insertions(+), 36 deletions(-) diff --git a/Main.tscn b/Main.tscn index 3542267db..449d1cb58 100644 --- a/Main.tscn +++ b/Main.tscn @@ -158,14 +158,12 @@ anchor_bottom = 1.0 custom_constants/separation = 0 [node name="TopMenuContainer" type="Panel" parent="MenuAndUI"] -editor/display_folded = true margin_right = 1152.0 margin_bottom = 28.0 rect_min_size = Vector2( 0, 28 ) custom_styles/panel = ExtResource( 3 ) [node name="MenuItems" type="HBoxContainer" parent="MenuAndUI/TopMenuContainer"] -editor/display_folded = true margin_left = 2.0 margin_top = 4.0 margin_right = 1010.0 @@ -193,9 +191,17 @@ mouse_default_cursor_shape = 2 text = "View" switch_on_hover = true -[node name="HelpMenu" type="MenuButton" parent="MenuAndUI/TopMenuContainer/MenuItems"] +[node name="ImageMenu" type="MenuButton" parent="MenuAndUI/TopMenuContainer/MenuItems"] margin_left = 102.0 -margin_right = 135.0 +margin_right = 144.0 +margin_bottom = 23.0 +mouse_default_cursor_shape = 2 +text = "Image" +switch_on_hover = true + +[node name="HelpMenu" type="MenuButton" parent="MenuAndUI/TopMenuContainer/MenuItems"] +margin_left = 148.0 +margin_right = 181.0 margin_bottom = 23.0 mouse_default_cursor_shape = 2 text = "Help" @@ -1671,6 +1677,53 @@ selected = 0 [node name="PreferencesDialog" parent="." instance=ExtResource( 61 )] +[node name="OutlineDialog" type="ConfirmationDialog" parent="."] +editor/display_folded = true +margin_right = 200.0 +margin_bottom = 70.0 + +[node name="OptionsContainer" type="GridContainer" parent="OutlineDialog"] +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -92.0 +margin_top = -36.0 +margin_right = 92.0 +margin_bottom = 5.0 +custom_constants/vseparation = 4 +custom_constants/hseparation = 4 +columns = 2 + +[node name="ThickLabel" type="Label" parent="OutlineDialog/OptionsContainer"] +margin_top = 1.0 +margin_right = 75.0 +margin_bottom = 16.0 +text = "Thickness:" + +[node name="ThickValue" type="SpinBox" parent="OutlineDialog/OptionsContainer"] +margin_left = 79.0 +margin_right = 143.0 +margin_bottom = 17.0 +min_value = 1.0 +max_value = 16384.0 +value = 1.0 +suffix = "px" + +[node name="OutlineColorLabel" type="Label" parent="OutlineDialog/OptionsContainer"] +margin_top = 23.0 +margin_right = 75.0 +margin_bottom = 38.0 +text = "Fill with color:" + +[node name="OutlineColor" type="ColorPickerButton" parent="OutlineDialog/OptionsContainer"] +margin_left = 79.0 +margin_top = 21.0 +margin_right = 143.0 +margin_bottom = 41.0 +rect_min_size = Vector2( 64, 20 ) +color = Color( 1, 0, 0, 1 ) + [node name="AboutDialog" type="AcceptDialog" parent="."] editor/display_folded = true margin_right = 284.0 @@ -1832,6 +1885,8 @@ visible = false [connection signal="confirmed" from="ScaleImage" to="." method="_on_ScaleImage_confirmed"] [connection signal="popup_hide" from="ScaleImage" to="." method="_can_draw_true"] [connection signal="popup_hide" from="PreferencesDialog" to="." method="_can_draw_true"] +[connection signal="confirmed" from="OutlineDialog" to="." method="_on_OutlineDialog_confirmed"] +[connection signal="popup_hide" from="OutlineDialog" to="." method="_can_draw_true"] [connection signal="popup_hide" from="AboutDialog" to="." method="_can_draw_true"] [connection signal="pressed" from="AboutDialog/AboutUI/Links/LinkButtons/Website" to="AboutDialog" method="_on_Website_pressed"] [connection signal="pressed" from="AboutDialog/AboutUI/Links/LinkButtons/GitHub" to="AboutDialog" method="_on_GitHub_pressed"] diff --git a/Scripts/Global.gd b/Scripts/Global.gd index 7a766e82d..5523d0b9d 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -136,6 +136,7 @@ var vertical_ruler : BaseButton var file_menu : MenuButton var edit_menu : MenuButton var view_menu : MenuButton +var image_menu : MenuButton var help_menu : MenuButton var cursor_position_label : Label var zoom_level_label : Label @@ -236,6 +237,7 @@ func _ready() -> void: file_menu = find_node_by_name(root, "FileMenu") edit_menu = find_node_by_name(root, "EditMenu") view_menu = find_node_by_name(root, "ViewMenu") + image_menu = find_node_by_name(root, "ImageMenu") help_menu = find_node_by_name(root, "HelpMenu") cursor_position_label = find_node_by_name(root, "CursorPosition") zoom_level_label = find_node_by_name(root, "ZoomLevel") diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 56883bb5d..ea3ac70d2 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -71,6 +71,9 @@ func _ready() -> void: "Show Rulers" : KEY_MASK_CMD + KEY_R, "Show Guides" : KEY_MASK_CMD + KEY_F } + var image_menu_items := { + "Outline" : 0 + } var help_menu_items := { "About Pixelorama" : 0 } @@ -95,6 +98,7 @@ func _ready() -> void: var file_menu : PopupMenu = Global.file_menu.get_popup() var edit_menu : PopupMenu = Global.edit_menu.get_popup() view_menu = Global.view_menu.get_popup() + var image_menu : PopupMenu = Global.image_menu.get_popup() var help_menu : PopupMenu = Global.help_menu.get_popup() var add_palette_menu : PopupMenu = Global.add_palette_button.get_child(0) @@ -114,6 +118,10 @@ func _ready() -> void: view_menu.set_item_checked(3, true) #Show Guides view_menu.hide_on_checkable_item_selection = false i = 0 + for item in image_menu_items.keys(): + image_menu.add_item(item, i, image_menu_items[item]) + i += 1 + i = 0 for item in help_menu_items.keys(): help_menu.add_item(item, i, help_menu_items[item]) i += 1 @@ -121,11 +129,12 @@ func _ready() -> void: file_menu.connect("id_pressed", self, "file_menu_id_pressed") edit_menu.connect("id_pressed", self, "edit_menu_id_pressed") view_menu.connect("id_pressed", self, "view_menu_id_pressed") + image_menu.connect("id_pressed", self, "image_menu_id_pressed") help_menu.connect("id_pressed", self, "help_menu_id_pressed") add_palette_menu.connect("id_pressed", self, "add_palette_menu_id_pressed") var root = get_tree().get_root() - #Node, left mouse shortcut, right mouse shortcut + # Node, left mouse shortcut, right mouse shortcut tools.append([Global.find_node_by_name(root, "Pencil"), "left_pencil_tool", "right_pencil_tool"]) tools.append([Global.find_node_by_name(root, "Eraser"), "left_eraser_tool", "right_eraser_tool"]) tools.append([Global.find_node_by_name(root, "Bucket"), "left_fill_tool", "right_fill_tool"]) @@ -139,12 +148,12 @@ func _ready() -> void: Global.left_color_picker.get_picker().move_child(Global.left_color_picker.get_picker().get_child(0), 1) Global.right_color_picker.get_picker().move_child(Global.right_color_picker.get_picker().get_child(0), 1) - #Options for Import + # Options for Import import_as_new_frame = CheckBox.new() import_as_new_frame.text = tr("IMPORT_FILE_LABEL") $ImportSprites.get_vbox().add_child(import_as_new_frame) - #Options for Export + # Options for Export var export_project_hbox := HBoxContainer.new() export_all_frames = CheckBox.new() export_all_frames.text = tr("EXPORT_ALLFRAMES_LABEL") @@ -185,84 +194,84 @@ func _input(event : InputEvent) -> void: if event.is_action_pressed("toggle_fullscreen"): OS.window_fullscreen = !OS.window_fullscreen - if event.is_action_pressed("redo_secondary"): #Shift + Ctrl + Z + if event.is_action_pressed("redo_secondary"): # Shift + Ctrl + Z redone = true Global.undo_redo.redo() redone = false if Global.has_focus: - for t in tools: #Handle tool shortcuts - if event.is_action_pressed(t[2]): #Shortcut for right button (with Alt) + for t in tools: # Handle tool shortcuts + if event.is_action_pressed(t[2]): # Shortcut for right button (with Alt) _on_Tool_pressed(t[0], false, false) - elif event.is_action_pressed(t[1]): #Shortcut for left button + elif event.is_action_pressed(t[1]): # Shortcut for left button _on_Tool_pressed(t[0], false, true) func _notification(what : int) -> void: - if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST: #Handle exit + if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST: # Handle exit $QuitDialog.call_deferred("popup_centered") Global.can_draw = false func file_menu_id_pressed(id : int) -> void: match id: - 0: #New + 0: # New $CreateNewImage.popup_centered() Global.can_draw = false - 1: #Open + 1: # Open $OpenSprite.popup_centered() Global.can_draw = false opensprite_file_selected = false - 2: #Save + 2: # Save if current_save_path == "": $SaveSprite.popup_centered() Global.can_draw = false else: _on_SaveSprite_file_selected(current_save_path) - 3: #Save as + 3: # Save as $SaveSprite.popup_centered() Global.can_draw = false - 4: #Import + 4: # Import $ImportSprites.popup_centered() Global.can_draw = false opensprite_file_selected = false - 5: #Export + 5: # Export if current_export_path == "": $ExportSprites.popup_centered() Global.can_draw = false else: export_project() - 6: #Export as + 6: # Export as $ExportSprites.popup_centered() Global.can_draw = false - 7: #Quit + 7: # Quit $QuitDialog.popup_centered() Global.can_draw = false func edit_menu_id_pressed(id : int) -> void: match id: - 0: #Undo + 0: # Undo Global.undo_redo.undo() - 1: #Redo + 1: # Redo redone = true Global.undo_redo.redo() redone = false - 2: #Scale Image + 2: # Scale Image $ScaleImage.popup_centered() Global.can_draw = false - 3: #Crop Image - #Use first layer as a starting rectangle + 3: # Crop Image + # Use first layer as a starting rectangle var used_rect : Rect2 = Global.canvas.layers[0][0].get_used_rect() - #However, if first layer is empty, loop through all layers until we find one that isn't + # However, if first layer is empty, loop through all layers until we find one that isn't var i := 0 while(i < Global.canvas.layers.size() - 1 && Global.canvas.layers[i][0].get_used_rect() == Rect2(0, 0, 0, 0)): i += 1 used_rect = Global.canvas.layers[i][0].get_used_rect() - #Merge all layers with content + # Merge all layers with content for j in range(Global.canvas.layers.size() - 1, i, -1): if Global.canvas.layers[j][0].get_used_rect() != Rect2(0, 0, 0, 0): used_rect = used_rect.merge(Global.canvas.layers[j][0].get_used_rect()) - #If no layer has any content, just return + # If no layer has any content, just return if used_rect == Rect2(0, 0, 0, 0): return @@ -271,7 +280,7 @@ func edit_menu_id_pressed(id : int) -> void: Global.undos += 1 Global.undo_redo.create_action("Scale") Global.undo_redo.add_do_property(Global.canvas, "size", Vector2(width, height).floor()) - #Loop through all the layers to crop them + # Loop through all the layers to crop them for j in range(Global.canvas.layers.size() - 1, -1, -1): var sprite : Image = Global.canvas.layers[j][0].get_rect(used_rect) Global.undo_redo.add_do_property(Global.canvas.layers[j][0], "data", sprite.data) @@ -281,7 +290,7 @@ func edit_menu_id_pressed(id : int) -> void: Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas]) Global.undo_redo.add_do_method(Global, "redo", [Global.canvas]) Global.undo_redo.commit_action() - 4: #Clear selection + 4: # Clear selection Global.canvas.handle_undo("Rectangle Select") Global.selection_rectangle.polygon[0] = Vector2.ZERO Global.selection_rectangle.polygon[1] = Vector2.ZERO @@ -303,24 +312,24 @@ func edit_menu_id_pressed(id : int) -> void: canvas.layers[canvas.current_layer_index][0].flip_y() canvas.layers[canvas.current_layer_index][0].lock() canvas.handle_redo("Draw") - 7: #Preferences + 7: # Preferences $PreferencesDialog.popup_centered() Global.can_draw = false func view_menu_id_pressed(id : int) -> void: match id: - 0: #Tile mode + 0: # Tile mode Global.tile_mode = !Global.tile_mode view_menu.set_item_checked(0, Global.tile_mode) - 1: #Show grid + 1: # Show grid Global.draw_grid = !Global.draw_grid view_menu.set_item_checked(1, Global.draw_grid) - 2: #Show rulers + 2: # Show rulers Global.show_rulers = !Global.show_rulers view_menu.set_item_checked(2, Global.show_rulers) Global.horizontal_ruler.visible = Global.show_rulers Global.vertical_ruler.visible = Global.show_rulers - 3: #Show guides + 3: # Show guides Global.show_guides = !Global.show_guides view_menu.set_item_checked(3, Global.show_guides) for canvas in Global.canvases: @@ -328,9 +337,14 @@ func view_menu_id_pressed(id : int) -> void: if guide is Guide: guide.visible = Global.show_guides +func image_menu_id_pressed(id : int) -> void: + match id: + 0: # Outline + $OutlineDialog.popup_centered() + func help_menu_id_pressed(id : int) -> void: match id: - 0: #About Pixelorama + 0: # About Pixelorama $AboutDialog.popup_centered() Global.can_draw = false @@ -638,6 +652,52 @@ func _on_ScaleImage_confirmed() -> void: Global.undo_redo.add_do_method(Global, "redo", [Global.canvas]) Global.undo_redo.commit_action() +func _on_OutlineDialog_confirmed() -> void: + var outline_color : Color = $OutlineDialog/OptionsContainer/OutlineColor.color + var thickness : int = $OutlineDialog/OptionsContainer/ThickValue.value + var image : Image = Global.canvas.layers[Global.canvas.current_layer_index][0] + if image.is_invisible(): + return + var new_image := Image.new() + new_image.copy_from(image) + new_image.lock() + + Global.canvas.handle_undo("Draw") + for xx in image.get_size().x: + for yy in image.get_size().y: + var pos = Vector2(xx, yy) + var current_pixel := image.get_pixelv(pos) + if current_pixel.a == 0: + continue + + for i in range(1, thickness + 1): + var new_pos : Vector2 = pos + Vector2.LEFT * i + if new_pos.x > 0: + var new_pixel = image.get_pixelv(new_pos) + if new_pixel.a == 0: + new_image.set_pixelv(new_pos, outline_color) + + new_pos = pos + Vector2.RIGHT * i + if new_pos.x < Global.canvas.size.x - 1: + var new_pixel = image.get_pixelv(new_pos) + if new_pixel.a == 0: + new_image.set_pixelv(new_pos, outline_color) + + new_pos = pos + Vector2.UP * i + if yy > 0: + var new_pixel = image.get_pixelv(new_pos) + if new_pixel.a == 0: + new_image.set_pixelv(new_pos, outline_color) + + new_pos = pos + Vector2.DOWN * i + if yy < Global.canvas.size.y - 1: + var new_pixel = image.get_pixelv(new_pos) + if new_pixel.a == 0: + new_image.set_pixelv(new_pos, outline_color) + + image.copy_from(new_image) + Global.canvas.handle_redo("Draw") + func _on_ImportSprites_popup_hide() -> void: if !opensprite_file_selected: Global.can_draw = true