diff --git a/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image b/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image new file mode 100644 index 000000000..b1356d3e5 Binary files /dev/null and b/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image differ diff --git a/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.md5 b/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.md5 new file mode 100644 index 000000000..657edc8e4 --- /dev/null +++ b/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.md5 @@ -0,0 +1,3 @@ +source_md5="f455fa104282b0fb31b168b668bb509b" +dest_md5="c8a263d7b4be7177025be77fb2ec15d3" + diff --git a/.import/grid.png-643b5c8878aaf0c84a360796789dae22.image b/.import/grid.png-643b5c8878aaf0c84a360796789dae22.image deleted file mode 100644 index 70deb678d..000000000 Binary files a/.import/grid.png-643b5c8878aaf0c84a360796789dae22.image and /dev/null differ diff --git a/.import/pixel.png-02d24c392cbedb7afeccdbe493729855.md5 b/.import/pixel.png-02d24c392cbedb7afeccdbe493729855.md5 new file mode 100644 index 000000000..a49f0afd0 --- /dev/null +++ b/.import/pixel.png-02d24c392cbedb7afeccdbe493729855.md5 @@ -0,0 +1,3 @@ +source_md5="5fa14a5c145b08d8c1f4a92c8b4014f2" +dest_md5="c73c58f53d6dace9fc04a1eb8ed9fd67" + diff --git a/.import/pixel.png-02d24c392cbedb7afeccdbe493729855.stex b/.import/pixel.png-02d24c392cbedb7afeccdbe493729855.stex new file mode 100644 index 000000000..038a79670 Binary files /dev/null and b/.import/pixel.png-02d24c392cbedb7afeccdbe493729855.stex differ diff --git a/Transparent Background.png b/Assets/Graphics/Transparent Background.png similarity index 100% rename from Transparent Background.png rename to Assets/Graphics/Transparent Background.png diff --git a/Assets/Graphics/Transparent Background.png.import b/Assets/Graphics/Transparent Background.png.import new file mode 100644 index 000000000..6c63960ff --- /dev/null +++ b/Assets/Graphics/Transparent Background.png.import @@ -0,0 +1,13 @@ +[remap] + +importer="image" +type="Image" +path="res://.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image" + +[deps] + +source_file="res://Assets/Graphics/Transparent Background.png" +dest_files=[ "res://.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image" ] + +[params] + diff --git a/Assets/Graphics/grid.png b/Assets/Graphics/grid.png deleted file mode 100644 index 859fb8c10..000000000 Binary files a/Assets/Graphics/grid.png and /dev/null differ diff --git a/Assets/Graphics/grid.png.import b/Assets/Graphics/grid.png.import deleted file mode 100644 index dba82b074..000000000 --- a/Assets/Graphics/grid.png.import +++ /dev/null @@ -1,13 +0,0 @@ -[remap] - -importer="image" -type="Image" -path="res://.import/grid.png-643b5c8878aaf0c84a360796789dae22.image" - -[deps] - -source_file="res://Assets/Graphics/grid.png" -dest_files=[ "res://.import/grid.png-643b5c8878aaf0c84a360796789dae22.image" ] - -[params] - diff --git a/Assets/Graphics/pixel.png b/Assets/Graphics/pixel.png new file mode 100644 index 000000000..e0c7ff938 Binary files /dev/null and b/Assets/Graphics/pixel.png differ diff --git a/Assets/Graphics/pixel.png.import b/Assets/Graphics/pixel.png.import new file mode 100644 index 000000000..b6e317513 --- /dev/null +++ b/Assets/Graphics/pixel.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/pixel.png-02d24c392cbedb7afeccdbe493729855.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Graphics/pixel.png" +dest_files=[ "res://.import/pixel.png-02d24c392cbedb7afeccdbe493729855.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/Main.tscn b/Main.tscn index d7fbdb12d..9a480905c 100644 --- a/Main.tscn +++ b/Main.tscn @@ -1,12 +1,15 @@ -[gd_scene load_steps=8 format=2] +[gd_scene load_steps=11 format=2] [ext_resource path="res://Scripts/Main.gd" type="Script" id=1] [ext_resource path="res://Main Theme.tres" type="Theme" id=2] [ext_resource path="res://Assets/Graphics/left.png" type="Texture" id=3] [ext_resource path="res://Assets/Graphics/right.png" type="Texture" id=4] -[ext_resource path="res://Canvas.tscn" type="PackedScene" id=5] -[ext_resource path="res://Scripts/CameraMovement.gd" type="Script" id=6] -[ext_resource path="res://Scripts/SelectionRectangle.gd" type="Script" id=7] +[ext_resource path="res://Prefabs/BrushButton.tscn" type="PackedScene" id=5] +[ext_resource path="res://Prefabs/Canvas.tscn" type="PackedScene" id=6] +[ext_resource path="res://Scripts/CameraMovement.gd" type="Script" id=7] +[ext_resource path="res://Scripts/SelectionRectangle.gd" type="Script" id=8] +[ext_resource path="res://Scripts/SecondViewport.gd" type="Script" id=9] +[ext_resource path="res://Scripts/AboutDialog.gd" type="Script" id=10] [node name="Control" type="Control"] anchor_right = 1.0 @@ -17,8 +20,10 @@ script = ExtResource( 1 ) anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 +custom_constants/separation = 0 [node name="ToolPanel" type="Panel" parent="UI"] +editor/display_folded = true margin_right = 320.0 margin_bottom = 600.0 rect_min_size = Vector2( 320, 0 ) @@ -32,7 +37,7 @@ size_flags_vertical = 3 [node name="MenusAndTools" type="VBoxContainer" parent="UI/ToolPanel/Tools"] margin_right = 320.0 -margin_bottom = 294.0 +margin_bottom = 270.0 size_flags_vertical = 3 [node name="MenuItems" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] @@ -62,7 +67,16 @@ mouse_default_cursor_shape = 2 theme = ExtResource( 2 ) text = "View" +[node name="HelpMenu" type="MenuButton" parent="UI/ToolPanel/Tools/MenusAndTools/MenuItems"] +margin_left = 125.0 +margin_right = 167.0 +margin_bottom = 20.0 +mouse_default_cursor_shape = 2 +theme = ExtResource( 2 ) +text = "Help" + [node name="ToolsContainer" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] +editor/display_folded = true margin_top = 24.0 margin_right = 320.0 margin_bottom = 44.0 @@ -122,19 +136,20 @@ button_mask = 3 text = "RectSelect" [node name="HSeparator" type="HSeparator" parent="UI/ToolPanel/Tools"] -margin_top = 298.0 +margin_top = 274.0 margin_right = 320.0 -margin_bottom = 302.0 +margin_bottom = 278.0 [node name="ToolOptions" type="HBoxContainer" parent="UI/ToolPanel/Tools"] -margin_top = 306.0 +editor/display_folded = true +margin_top = 282.0 margin_right = 320.0 -margin_bottom = 600.0 +margin_bottom = 552.0 size_flags_vertical = 3 [node name="LeftToolOptions" type="VBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions"] margin_right = 154.0 -margin_bottom = 294.0 +margin_bottom = 270.0 size_flags_horizontal = 3 [node name="LeftLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] @@ -161,6 +176,7 @@ size_flags_horizontal = 0 size_flags_vertical = 0 [node name="BrushSize" type="HBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] +editor/display_folded = true margin_top = 82.0 margin_right = 154.0 margin_bottom = 106.0 @@ -180,15 +196,51 @@ min_value = 1.0 value = 1.0 suffix = "px" +[node name="ColorComesFrom" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] +margin_top = 110.0 +margin_right = 154.0 +margin_bottom = 124.0 +text = "Brush's color from" + +[node name="InterpolateColor" type="HBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] +margin_top = 128.0 +margin_right = 154.0 +margin_bottom = 144.0 + +[node name="BrushColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/InterpolateColor"] +margin_top = 1.0 +margin_right = 37.0 +margin_bottom = 15.0 +rect_pivot_offset = Vector2( -90, -47 ) +text = "Brush" + +[node name="LeftInterpolateFactor" type="HSlider" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/InterpolateColor"] +margin_left = 41.0 +margin_right = 117.0 +margin_bottom = 16.0 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 0.5 +ticks_on_borders = true + +[node name="SelectedColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/InterpolateColor"] +margin_left = 121.0 +margin_top = 1.0 +margin_right = 154.0 +margin_bottom = 15.0 +rect_pivot_offset = Vector2( -90, -47 ) +text = "Color" + [node name="VSeparator" type="VSeparator" parent="UI/ToolPanel/Tools/ToolOptions"] margin_left = 158.0 margin_right = 162.0 -margin_bottom = 294.0 +margin_bottom = 270.0 [node name="RightToolOptions" type="VBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions"] margin_left = 166.0 margin_right = 320.0 -margin_bottom = 294.0 +margin_bottom = 270.0 size_flags_horizontal = 3 [node name="RightLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] @@ -233,13 +285,107 @@ min_value = 1.0 value = 1.0 suffix = "px" +[node name="ColorComesFrom" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] +margin_top = 110.0 +margin_right = 154.0 +margin_bottom = 124.0 +text = "Brush's color from" + +[node name="InterpolateColor" type="HBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] +margin_top = 128.0 +margin_right = 154.0 +margin_bottom = 144.0 + +[node name="BrushColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/InterpolateColor"] +margin_top = 1.0 +margin_right = 37.0 +margin_bottom = 15.0 +rect_pivot_offset = Vector2( -90, -47 ) +text = "Brush" + +[node name="RightInterpolateFactor" type="HSlider" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/InterpolateColor"] +margin_left = 41.0 +margin_right = 117.0 +margin_bottom = 16.0 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 0.5 +ticks_on_borders = true + +[node name="SelectedColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/InterpolateColor"] +margin_left = 121.0 +margin_top = 1.0 +margin_right = 154.0 +margin_bottom = 15.0 +rect_pivot_offset = Vector2( -90, -47 ) +text = "Color" + +[node name="HSeparator2" type="HSeparator" parent="UI/ToolPanel/Tools"] +margin_top = 556.0 +margin_right = 320.0 +margin_bottom = 560.0 + +[node name="BrushesContainer" type="ScrollContainer" parent="UI/ToolPanel/Tools"] +margin_top = 564.0 +margin_right = 320.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 +scroll_vertical_enabled = false + +[node name="BrushHBoxContainer" type="HBoxContainer" parent="UI/ToolPanel/Tools/BrushesContainer"] +margin_right = 36.0 +margin_bottom = 36.0 + +[node name="PixelBrushButton" parent="UI/ToolPanel/Tools/BrushesContainer/BrushHBoxContainer" instance=ExtResource( 5 )] + [node name="CanvasAndTimeline" type="VBoxContainer" parent="UI"] -margin_left = 324.0 -margin_right = 860.0 +margin_left = 320.0 +margin_right = 864.0 margin_bottom = 600.0 size_flags_horizontal = 3 -[node name="ViewportContainer" type="ViewportContainer" parent="UI/CanvasAndTimeline"] +[node name="HBoxContainer" type="HBoxContainer" parent="UI/CanvasAndTimeline"] +margin_right = 544.0 +margin_bottom = 464.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ViewportContainer" type="ViewportContainer" parent="UI/CanvasAndTimeline/HBoxContainer"] +margin_right = 544.0 +margin_bottom = 464.0 +mouse_default_cursor_shape = 3 +size_flags_horizontal = 3 +size_flags_vertical = 3 +stretch = true + +[node name="Viewport" type="Viewport" parent="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer"] +size = Vector2( 544, 464 ) +handle_input_locally = false +render_target_update_mode = 3 + +[node name="Canvas" parent="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer/Viewport" instance=ExtResource( 6 )] + +[node name="Camera2D" type="Camera2D" parent="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer/Viewport"] +current = true +zoom = Vector2( 0.15, 0.15 ) +script = ExtResource( 7 ) + +[node name="SelectionRectangle" type="Polygon2D" parent="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer/Viewport"] +z_index = 1 +color = Color( 0.0823529, 0.694118, 0.623529, 0.592157 ) +polygon = PoolVector2Array( 0, 0, 0, 0, 0, 0, 0, 0 ) +script = ExtResource( 8 ) + +[node name="ViewportSeparator" type="VSeparator" parent="UI/CanvasAndTimeline/HBoxContainer"] +visible = false +margin_left = 532.0 +margin_right = 536.0 +margin_bottom = 464.0 + +[node name="ViewportContainer2" type="ViewportContainer" parent="UI/CanvasAndTimeline/HBoxContainer"] +visible = false +margin_left = 270.0 margin_right = 536.0 margin_bottom = 464.0 mouse_default_cursor_shape = 3 @@ -247,27 +393,21 @@ size_flags_horizontal = 3 size_flags_vertical = 3 stretch = true -[node name="Viewport" type="Viewport" parent="UI/CanvasAndTimeline/ViewportContainer"] -size = Vector2( 536, 464 ) +[node name="Viewport" type="Viewport" parent="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer2"] +size = Vector2( 266, 464 ) handle_input_locally = false -render_target_update_mode = 3 +render_target_update_mode = 0 +script = ExtResource( 9 ) -[node name="Canvas" parent="UI/CanvasAndTimeline/ViewportContainer/Viewport" instance=ExtResource( 5 )] - -[node name="Camera2D" type="Camera2D" parent="UI/CanvasAndTimeline/ViewportContainer/Viewport"] +[node name="Camera2D2" type="Camera2D" parent="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer2/Viewport"] current = true zoom = Vector2( 0.15, 0.15 ) -script = ExtResource( 6 ) - -[node name="SelectionRectangle" type="Polygon2D" parent="UI/CanvasAndTimeline/ViewportContainer/Viewport"] -z_index = 1 -color = Color( 0.0823529, 0.694118, 0.623529, 0.592157 ) -polygon = PoolVector2Array( 0, 0, 0, 0, 0, 0, 0, 0 ) script = ExtResource( 7 ) [node name="AnimationTimeline" type="Panel" parent="UI/CanvasAndTimeline"] +editor/display_folded = true margin_top = 468.0 -margin_right = 536.0 +margin_right = 544.0 margin_bottom = 600.0 rect_min_size = Vector2( 0, 132 ) size_flags_horizontal = 3 @@ -278,12 +418,12 @@ anchor_bottom = 1.0 size_flags_horizontal = 3 [node name="ButtonContainer" type="CenterContainer" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer"] -margin_right = 536.0 +margin_right = 544.0 margin_bottom = 24.0 [node name="AnimationButtons" type="HBoxContainer" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/ButtonContainer"] -margin_left = 90.0 -margin_right = 446.0 +margin_left = 94.0 +margin_right = 450.0 margin_bottom = 24.0 [node name="LoopLabel" type="Label" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/ButtonContainer/AnimationButtons"] @@ -325,25 +465,25 @@ margin_right = 356.0 margin_bottom = 24.0 hint_tooltip = "How many frames per second should the animation preview be? The more FPS, the faster the animation plays." mouse_default_cursor_shape = 2 -min_value = 0.01 -step = 0.01 +min_value = 0.1 +step = 0.1 value = 1.0 suffix = "FPS" [node name="HSeparator" type="HSeparator" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer"] margin_top = 28.0 -margin_right = 536.0 +margin_right = 544.0 margin_bottom = 32.0 [node name="CenterContainer" type="CenterContainer" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer"] editor/display_folded = true margin_top = 36.0 -margin_right = 536.0 +margin_right = 544.0 margin_bottom = 56.0 [node name="FrameButtons" type="HBoxContainer" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/CenterContainer"] -margin_left = 209.0 -margin_right = 326.0 +margin_left = 213.0 +margin_right = 330.0 margin_bottom = 20.0 [node name="AddFrame" type="Button" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/CenterContainer/FrameButtons"] @@ -392,12 +532,12 @@ text = "Cl" [node name="HSeparator2" type="HSeparator" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer"] margin_top = 60.0 -margin_right = 536.0 +margin_right = 544.0 margin_bottom = 64.0 [node name="ScrollContainer" type="ScrollContainer" parent="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer"] margin_top = 68.0 -margin_right = 536.0 +margin_right = 544.0 margin_bottom = 132.0 size_flags_horizontal = 3 size_flags_vertical = 3 @@ -414,11 +554,12 @@ rect_min_size = Vector2( 160, 0 ) [node name="LayersAndMisc" type="VBoxContainer" parent="UI/LayerPanel"] anchor_right = 1.0 anchor_bottom = 1.0 +custom_constants/separation = 3 [node name="ScrollContainer" type="ScrollContainer" parent="UI/LayerPanel/LayersAndMisc"] editor/display_folded = true margin_right = 160.0 -margin_bottom = 375.0 +margin_bottom = 382.0 size_flags_horizontal = 3 size_flags_vertical = 3 @@ -498,15 +639,15 @@ disabled = true text = "M" [node name="HSeparator" type="HSeparator" parent="UI/LayerPanel/LayersAndMisc"] -margin_top = 379.0 +margin_top = 385.0 margin_right = 160.0 -margin_bottom = 383.0 +margin_bottom = 389.0 [node name="OnionSkinningButtons" type="VBoxContainer" parent="UI/LayerPanel/LayersAndMisc"] editor/display_folded = true -margin_top = 387.0 +margin_top = 392.0 margin_right = 160.0 -margin_bottom = 520.0 +margin_bottom = 525.0 [node name="OnionSkinning" type="Label" parent="UI/LayerPanel/LayersAndMisc/OnionSkinningButtons"] margin_right = 160.0 @@ -537,26 +678,26 @@ margin_bottom = 133.0 text = "Blue-Red Mode" [node name="HSeparator2" type="HSeparator" parent="UI/LayerPanel/LayersAndMisc"] -margin_top = 524.0 +margin_top = 528.0 margin_right = 160.0 -margin_bottom = 528.0 +margin_bottom = 532.0 [node name="CursorPosition" type="Label" parent="UI/LayerPanel/LayersAndMisc"] -margin_top = 532.0 +margin_top = 535.0 margin_right = 160.0 -margin_bottom = 546.0 +margin_bottom = 549.0 text = "[64x64]" [node name="ZoomLevel" type="Label" parent="UI/LayerPanel/LayersAndMisc"] -margin_top = 550.0 +margin_top = 552.0 margin_right = 160.0 -margin_bottom = 564.0 +margin_bottom = 566.0 text = "Zoom: x7.81" [node name="CurrentFrame" type="Label" parent="UI/LayerPanel/LayersAndMisc"] -margin_top = 568.0 +margin_top = 569.0 margin_right = 160.0 -margin_bottom = 582.0 +margin_bottom = 583.0 text = "Current frame: 1/1" [node name="EmptyLabel" type="Label" parent="UI/LayerPanel/LayersAndMisc"] @@ -564,6 +705,16 @@ margin_top = 586.0 margin_right = 160.0 margin_bottom = 600.0 +[node name="SplitScreenButton" type="Button" parent="."] +anchor_left = 1.0 +anchor_right = 1.0 +margin_left = -184.0 +margin_right = -164.0 +margin_bottom = 20.0 +size_flags_vertical = 0 +toggle_mode = true +text = "<" + [node name="CreateNewImage" type="ConfirmationDialog" parent="."] editor/display_folded = true margin_right = 200.0 @@ -769,6 +920,76 @@ text = "Nearest" items = [ "Nearest", null, false, 0, null, "Bilinear", null, false, 1, null, "Cubic", null, false, 2, null, "Trilinear", null, false, 3, null, "Lanczos", null, true, 4, null ] selected = 0 +[node name="AboutDialog" type="AcceptDialog" parent="."] +editor/display_folded = true +margin_right = 284.0 +margin_bottom = 186.0 +window_title = "About Pixelorama" +script = ExtResource( 10 ) + +[node name="AboutUI" type="VBoxContainer" parent="AboutDialog"] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 276.0 +margin_bottom = 150.0 + +[node name="Pixelorama" type="Label" parent="AboutDialog/AboutUI"] +margin_right = 339.0 +margin_bottom = 31.0 +text = "Pixelorama v0.3 +" +align = 1 + +[node name="MadeBy" type="Label" parent="AboutDialog/AboutUI"] +margin_top = 35.0 +margin_right = 339.0 +margin_bottom = 83.0 +text = "Your Free and Open Source Sprite Editor +Developed by Orama Interactive +" +align = 1 + +[node name="Links" type="CenterContainer" parent="AboutDialog/AboutUI"] +margin_top = 87.0 +margin_right = 339.0 +margin_bottom = 107.0 + +[node name="LinkButtons" type="HBoxContainer" parent="AboutDialog/AboutUI/Links"] +margin_left = 35.0 +margin_right = 303.0 +margin_bottom = 20.0 + +[node name="Website" type="Button" parent="AboutDialog/AboutUI/Links/LinkButtons"] +margin_right = 65.0 +margin_bottom = 20.0 +text = "Website" + +[node name="GitHub" type="Button" parent="AboutDialog/AboutUI/Links/LinkButtons"] +margin_left = 69.0 +margin_right = 162.0 +margin_bottom = 20.0 +text = "GitHub Repo" + +[node name="Donate" type="Button" parent="AboutDialog/AboutUI/Links/LinkButtons"] +margin_left = 166.0 +margin_right = 224.0 +margin_bottom = 20.0 +text = "Donate" + +[node name="Blog" type="Button" parent="AboutDialog/AboutUI/Links/LinkButtons"] +margin_left = 228.0 +margin_right = 268.0 +margin_bottom = 20.0 +text = "Blog" + +[node name="Copyright" type="Label" parent="AboutDialog/AboutUI"] +margin_top = 111.0 +margin_right = 339.0 +margin_bottom = 142.0 +text = " +Copyright 2019 - Orama Interactive" +align = 1 + [node name="AnimationTimer" type="Timer" parent="."] [connection signal="toggled" from="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/LeftIndicatorCheckbox" to="." method="_on_LeftIndicatorCheckbox_toggled"] [connection signal="popup_closed" from="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/LeftColorPickerButton" to="." method="_can_draw_true"] @@ -778,8 +999,10 @@ selected = 0 [connection signal="popup_closed" from="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/RightColorPickerButton" to="." method="_can_draw_true"] [connection signal="pressed" from="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/RightColorPickerButton" to="." method="_can_draw_false"] [connection signal="value_changed" from="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/BrushSize/RightBrushSizeEdit" to="." method="_on_RightBrushSizeEdit_value_changed"] -[connection signal="mouse_entered" from="UI/CanvasAndTimeline/ViewportContainer" to="." method="_on_ViewportContainer_mouse_entered"] -[connection signal="mouse_exited" from="UI/CanvasAndTimeline/ViewportContainer" to="." method="_on_ViewportContainer_mouse_exited"] +[connection signal="mouse_entered" from="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer" to="." method="_on_ViewportContainer_mouse_entered"] +[connection signal="mouse_exited" from="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer" to="." method="_on_ViewportContainer_mouse_exited"] +[connection signal="mouse_entered" from="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer2" to="." method="_on_ViewportContainer_mouse_entered"] +[connection signal="mouse_exited" from="UI/CanvasAndTimeline/HBoxContainer/ViewportContainer2" to="." method="_on_ViewportContainer_mouse_exited"] [connection signal="pressed" from="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/ButtonContainer/AnimationButtons/LoopAnim" to="." method="_on_LoopAnim_pressed"] [connection signal="toggled" from="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/ButtonContainer/AnimationButtons/PlayBackwards" to="." method="_on_PlayBackwards_toggled"] [connection signal="toggled" from="UI/CanvasAndTimeline/AnimationTimeline/TimelineContainer/ButtonContainer/AnimationButtons/PlayForward" to="." method="_on_PlayForward_toggled"] @@ -798,6 +1021,7 @@ selected = 0 [connection signal="value_changed" from="UI/LayerPanel/LayersAndMisc/OnionSkinningButtons/PastOnionSkinning" to="." method="_on_PastOnionSkinning_value_changed"] [connection signal="value_changed" from="UI/LayerPanel/LayersAndMisc/OnionSkinningButtons/FutureOnionSkinning" to="." method="_on_FutureOnionSkinning_value_changed"] [connection signal="toggled" from="UI/LayerPanel/LayersAndMisc/OnionSkinningButtons/BlueRedMode" to="." method="_on_BlueRedMode_toggled"] +[connection signal="toggled" from="SplitScreenButton" to="." method="_on_SplitScreenButton_toggled"] [connection signal="confirmed" from="CreateNewImage" to="." method="_on_CreateNewImage_confirmed"] [connection signal="popup_hide" from="CreateNewImage" to="." method="_can_draw_true"] [connection signal="file_selected" from="OpenSprite" to="." method="_on_OpenSprite_file_selected"] @@ -810,4 +1034,9 @@ selected = 0 [connection signal="popup_hide" from="ExportSprites" to="." method="_can_draw_true"] [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="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"] +[connection signal="pressed" from="AboutDialog/AboutUI/Links/LinkButtons/Donate" to="AboutDialog" method="_on_Donate_pressed"] +[connection signal="pressed" from="AboutDialog/AboutUI/Links/LinkButtons/Blog" to="AboutDialog" method="_on_Blog_pressed"] [connection signal="timeout" from="AnimationTimer" to="." method="_on_AnimationTimer_timeout"] diff --git a/Prefabs/BrushButton.tscn b/Prefabs/BrushButton.tscn new file mode 100644 index 000000000..6c9d135dd --- /dev/null +++ b/Prefabs/BrushButton.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://Scripts/BrushButton.gd" type="Script" id=1] +[ext_resource path="res://Assets/Graphics/pixel.png" type="Texture" id=2] + +[node name="BrushButton" type="Button"] +margin_right = 36.0 +margin_bottom = 36.0 +rect_min_size = Vector2( 36, 36 ) +button_mask = 3 +script = ExtResource( 1 ) + +[node name="BrushTexture" type="TextureRect" parent="."] +margin_left = 2.0 +margin_top = 2.0 +margin_right = 34.0 +margin_bottom = 34.0 +rect_min_size = Vector2( 32, 32 ) +texture = ExtResource( 2 ) +expand = true +stretch_mode = 6 +[connection signal="pressed" from="." to="." method="_on_BrushButton_pressed"] diff --git a/Canvas.tscn b/Prefabs/Canvas.tscn similarity index 100% rename from Canvas.tscn rename to Prefabs/Canvas.tscn diff --git a/FrameButton.tscn b/Prefabs/FrameButton.tscn similarity index 100% rename from FrameButton.tscn rename to Prefabs/FrameButton.tscn diff --git a/LayerContainer.tscn b/Prefabs/LayerContainer.tscn similarity index 100% rename from LayerContainer.tscn rename to Prefabs/LayerContainer.tscn diff --git a/Scripts/AboutDialog.gd b/Scripts/AboutDialog.gd new file mode 100644 index 000000000..0e9a09825 --- /dev/null +++ b/Scripts/AboutDialog.gd @@ -0,0 +1,19 @@ +extends AcceptDialog + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + var current_version : String = ProjectSettings.get_setting("application/config/Version") + $AboutUI/Pixelorama.text = "Pixelorama %s\n" % current_version + +func _on_Website_pressed() -> void: + OS.shell_open("http://oramagamestudios.com/") + +func _on_GitHub_pressed() -> void: + OS.shell_open("https://github.com/OverloadedOrama/Pixelorama") + +func _on_Donate_pressed() -> void: + OS.shell_open("https://www.paypal.com/paypalme2/OverloadedOrama") + OS.shell_open("https://ko-fi.com/overloadedorama") + +func _on_Blog_pressed() -> void: + OS.shell_open("https://functionoverload590613498.wordpress.com/") diff --git a/Scripts/BrushButton.gd b/Scripts/BrushButton.gd new file mode 100644 index 000000000..37bc23ba8 --- /dev/null +++ b/Scripts/BrushButton.gd @@ -0,0 +1,14 @@ +extends Button + +var brush_type = Global.BRUSH_TYPES.PIXEL +var custom_brush_index := -1 + +func _on_BrushButton_pressed() -> void: + if Input.is_action_just_released("left_mouse"): + Global.current_left_brush_type = brush_type + if custom_brush_index > -1: + Global.custom_left_brush_index = custom_brush_index + elif Input.is_action_just_released("right_mouse"): + Global.current_right_brush_type = brush_type + if custom_brush_index > -1: + Global.custom_right_brush_index = custom_brush_index diff --git a/Scripts/CameraMovement.gd b/Scripts/CameraMovement.gd index 749be2788..2dc219415 100644 --- a/Scripts/CameraMovement.gd +++ b/Scripts/CameraMovement.gd @@ -2,16 +2,22 @@ extends Camera2D var zoom_min := Vector2(0.005, 0.005) var zoom_max := Vector2.ONE - +var viewport_container : ViewportContainer var drag := false +func _ready() -> void: + viewport_container = get_parent().get_parent() + func _input(event) -> void: - if Global.can_draw && Global.has_focus: - if event.is_action_pressed("camera_drag"): - drag = true - elif event.is_action_released("camera_drag"): - drag = false - elif event.is_action_pressed("zoom_in"): # Wheel Up Event + var mouse_pos := viewport_container.get_local_mouse_position() + var viewport_size := viewport_container.rect_size + if event.is_action_pressed("camera_drag"): + drag = true + elif event.is_action_released("camera_drag"): + drag = false + + if Global.can_draw && Global.has_focus && Rect2(Vector2.ZERO, viewport_size).has_point(mouse_pos): + if event.is_action_pressed("zoom_in"): # Wheel Up Event zoom_camera(-1) elif event.is_action_pressed("zoom_out"): # Wheel Down Event zoom_camera(1) @@ -27,5 +33,5 @@ func zoom_camera(dir : int) -> void: if zoom > zoom_max: zoom = zoom_max - - Global.zoom_level_label.text = "Zoom: x%s" % [stepify(1 / zoom.x, 0.01)] \ No newline at end of file + if name == "Camera2D": + Global.zoom_level_label.text = "Zoom: x%s" % [stepify(1 / zoom.x, 0.01)] \ No newline at end of file diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index e91c31e88..35d540710 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -23,7 +23,7 @@ func _ready() -> void: Global.can_draw = false #Background trans_background = ImageTexture.new() - trans_background.create_from_image(load("res://Transparent Background.png"), 0) + trans_background.create_from_image(load("res://Assets/Graphics/Transparent Background.png"), 0) #The sprite itself if layers.empty(): @@ -39,7 +39,7 @@ func _ready() -> void: generate_layer_panels() - frame_button = load("res://FrameButton.tscn").instance() + frame_button = load("res://Prefabs/FrameButton.tscn").instance() frame_button.name = "Frame_%s" % frame frame_button.get_node("FrameButton").frame = frame frame_button.get_node("FrameID").text = str(frame + 1) @@ -50,6 +50,23 @@ func _ready() -> void: camera_zoom() +func camera_zoom() -> void: + #Set camera offset to the center of canvas + Global.camera.offset = size / 2 + Global.camera2.offset = size / 2 + #Set camera zoom based on the sprite size + var bigger = max(size.x, size.y) + var zoom_max := Vector2(bigger, bigger) * 0.01 + if zoom_max > Vector2.ONE: + Global.camera.zoom_max = zoom_max + Global.camera2.zoom_max = zoom_max + else: + Global.camera.zoom_max = Vector2.ONE + Global.camera2.zoom_max = Vector2.ONE + Global.camera.zoom = Vector2(bigger, bigger) * 0.002 + Global.camera2.zoom = Vector2(bigger, bigger) * 0.002 + Global.zoom_level_label.text = "Zoom: x%s" % [stepify(1 / Global.camera.zoom.x, 0.01)] + # warning-ignore:unused_argument func _process(delta) -> void: sprite_changed_this_frame = false @@ -147,8 +164,7 @@ func _process(delta) -> void: for xx in range(start_pos.x, end_pos.x): for yy in range(start_pos.y, end_pos.y): - if xx >= location.x && xx < size.x && yy >= location.y && yy < size.y: - Global.selected_pixels.append(Vector2(xx, yy)) + Global.selected_pixels.append(Vector2(xx, yy)) is_making_selection = "None" if sprite_changed_this_frame: @@ -255,7 +271,7 @@ func generate_layer_panels() -> void: Global.remove_layer_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND for i in range(layers.size() -1, -1, -1): - var layer_container = load("res://LayerContainer.tscn").instance() + var layer_container = load("res://Prefabs/LayerContainer.tscn").instance() #layer_names.insert(i, "Layer %s" % i) layers[i][2] = "Layer %s" % i layer_container.i = i @@ -265,19 +281,6 @@ func generate_layer_panels() -> void: layer_container.get_child(0).get_child(1).texture = layers[i][1] Global.vbox_layer_container.add_child(layer_container) -func camera_zoom() -> void: - #Set camera offset to the center of canvas - Global.camera.offset = size / 2 - #Set camera zoom based on the sprite size - var bigger = max(size.x, size.y) - var zoom_max := Vector2(bigger, bigger) * 0.01 - if zoom_max > Vector2.ONE: - Global.camera.zoom_max = zoom_max - else: - Global.camera.zoom_max = Vector2.ONE - Global.camera.zoom = Vector2(bigger, bigger) * 0.002 - Global.zoom_level_label.text = "Zoom: x%s" % [stepify(1 / $"../Camera2D".zoom.x, 0.01)] - func pencil_and_eraser(mouse_pos : Vector2, color : Color, current_mouse_button : String) -> void: if Input.is_key_pressed(KEY_SHIFT): if !is_making_line: @@ -290,55 +293,85 @@ func pencil_and_eraser(mouse_pos : Vector2, color : Color, current_mouse_button is_making_line = true else: var brush_size := 1 + var brush_type = Global.BRUSH_TYPES.PIXEL + var brush_index := -1 + var interpolate_factor := 0.5 if current_mouse_button == "left_mouse": brush_size = Global.left_brush_size + brush_type = Global.current_left_brush_type + brush_index = Global.custom_left_brush_index + interpolate_factor = Global.left_interpolate_slider.value elif current_mouse_button == "right_mouse": brush_size = Global.right_brush_size + brush_type = Global.current_right_brush_type + brush_index = Global.custom_right_brush_index + interpolate_factor = Global.right_interpolate_slider.value if is_making_line: - fill_gaps(mouse_pos, color, brush_size) + fill_gaps(mouse_pos, color, brush_size, brush_type, brush_index, interpolate_factor) is_making_line = false line_2d.queue_free() else: if point_in_rectangle(mouse_pos, location, location + size): mouse_inside_canvas = true #Draw - draw_pixel(mouse_pos, color, brush_size) - fill_gaps(mouse_pos, color, brush_size) #Fill the gaps + draw_pixel(mouse_pos, color, brush_size, brush_type, brush_index, interpolate_factor) + fill_gaps(mouse_pos, color, brush_size, brush_type, brush_index, interpolate_factor) #Fill the gaps #If mouse is not inside bounds but it used to be, fill the gaps elif point_in_rectangle(previous_mouse_pos, location, location + size): - fill_gaps(mouse_pos, color, brush_size) + fill_gaps(mouse_pos, color, brush_size, brush_type, brush_index, interpolate_factor) -func draw_pixel(pos : Vector2, color : Color, brush_size : int) -> void: +func draw_pixel(pos : Vector2, color : Color, brush_size : int, brush_type : int, brush_index : int, interpolate_factor : float) -> void: if Global.can_draw && Global.has_focus && Global.current_frame == frame: - #If there is a selection and current pixel is not in it var west_limit := location.x var east_limit := location.x + size.x var north_limit := location.y var south_limit := location.y + size.y - if Global.selected_pixels.size() != 0: + if Global.selected_pixels.size() != 0: #If there is a selection and current pixel position is not in it west_limit = max(west_limit, Global.selection_rectangle.polygon[0].x) east_limit = min(east_limit, Global.selection_rectangle.polygon[2].x) north_limit = max(north_limit, Global.selection_rectangle.polygon[0].y) south_limit = min(south_limit, Global.selection_rectangle.polygon[2].y) - var start_pos_x = pos.x - (brush_size >> 1) - var start_pos_y = pos.y - (brush_size >> 1) - for cur_pos_x in range(start_pos_x, start_pos_x + brush_size): - #layers[current_layer_index][0].set_pixel(cur_pos_x, pos.y, color) - for cur_pos_y in range(start_pos_y, start_pos_y + brush_size): - if point_in_rectangle(Vector2(cur_pos_x, cur_pos_y), Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)): - if layers[current_layer_index][0].get_pixel(cur_pos_x, cur_pos_y) != color: #don't draw the same pixel over and over - layers[current_layer_index][0].set_pixel(cur_pos_x, cur_pos_y, color) - #layers[current_layer_index][0].set_pixelv(pos, color) - sprite_changed_this_frame = true - -func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool: - return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y + var start_pos_x + var start_pos_y + var end_pos_x + var end_pos_y + + match(brush_type): + Global.BRUSH_TYPES.PIXEL: + start_pos_x = pos.x - (brush_size >> 1) + start_pos_y = pos.y - (brush_size >> 1) + end_pos_x = start_pos_x + brush_size + end_pos_y = start_pos_y + brush_size + for cur_pos_x in range(start_pos_x, end_pos_x): + for cur_pos_y in range(start_pos_y, end_pos_y): + if point_in_rectangle(Vector2(cur_pos_x, cur_pos_y), Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)): + if layers[current_layer_index][0].get_pixel(cur_pos_x, cur_pos_y) != color: #don't draw the same pixel over and over + layers[current_layer_index][0].set_pixel(cur_pos_x, cur_pos_y, color) + sprite_changed_this_frame = true + + Global.BRUSH_TYPES.CUSTOM: + var custom_brush := Image.new() + custom_brush.copy_from(Global.custom_brushes[brush_index]) + var custom_brush_blended := blend_image_with_color(custom_brush, color, interpolate_factor) + var custom_brush_size = custom_brush_blended.get_size() + custom_brush_blended.resize(custom_brush_size.x * brush_size, custom_brush_size.y * brush_size, Image.INTERPOLATE_NEAREST) + custom_brush_size = custom_brush_blended.get_size() + var dst : Vector2 = pos - custom_brush_size / 4 + var src_rect := Rect2(Vector2.ZERO, custom_brush_size) + #src_rect = src_rect.clip(Rect2(west_limit - dst.x, north_limit - dst.y, east_limit - custom_brush_size.x, south_limit - custom_brush_size.y)) + + if color.a > 0: #If it's the pencil + layers[current_layer_index][0].blend_rect(custom_brush_blended, src_rect, dst) + else: #if it's transparent - if it's the eraser + layers[current_layer_index][0].blit_rect_mask(custom_brush_blended, custom_brush, src_rect, dst) + layers[current_layer_index][0].lock() + update_texture(current_layer_index) #Bresenham's Algorithm #Thanks to https://godotengine.org/qa/35276/tile-based-line-drawing-algorithm-efficiency -func fill_gaps(mouse_pos : Vector2, color : Color, brush_size : int) -> void: +func fill_gaps(mouse_pos : Vector2, color : Color, brush_size : int, brush_type : int, brush_index : int, interpolate_factor : float) -> void: var previous_mouse_pos_floored = previous_mouse_pos.floor() var mouse_pos_floored = mouse_pos.floor() mouse_pos_floored.x = clamp(mouse_pos_floored.x, location.x - 1, location.x + size.x) @@ -352,7 +385,7 @@ func fill_gaps(mouse_pos : Vector2, color : Color, brush_size : int) -> void: var x = previous_mouse_pos_floored.x var y = previous_mouse_pos_floored.y while !(x == mouse_pos_floored.x && y == mouse_pos_floored.y): - draw_pixel(Vector2(x, y), color, brush_size) + draw_pixel(Vector2(x, y), color, brush_size, brush_type, brush_index, interpolate_factor) e2 = err << 1 if e2 >= dy: err += dy @@ -393,13 +426,35 @@ func flood_fill(pos : Vector2, target_color : Color, replace_color : Color) -> v east += Vector2.RIGHT for px in range(west.x + 1, east.x): var p := Vector2(px, n.y) - draw_pixel(p, replace_color, 1) + #Draw + layers[current_layer_index][0].set_pixelv(p, replace_color) var north := p + Vector2.UP var south := p + Vector2.DOWN if north.y >= north_limit && layers[current_layer_index][0].get_pixelv(north) == target_color: q.append(north) if south.y < south_limit && layers[current_layer_index][0].get_pixelv(south) == target_color: q.append(south) + sprite_changed_this_frame = true + +func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool: + return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y + +func blend_image_with_color(image : Image, color : Color, interpolate_factor : float) -> Image: + var blended_image := Image.new() + blended_image.copy_from(image) + var size := image.get_size() + blended_image.lock() + for xx in size.x: + for yy in size.y: + if color.a > 0: #If it's the pencil + var current_color := blended_image.get_pixel(xx, yy) + if current_color.a > 0: + #var blended_color = current_color.blend(color) + var new_color := current_color.linear_interpolate(color, interpolate_factor) + blended_image.set_pixel(xx, yy, new_color) + else: #If color is transparent - if it's the eraser + blended_image.set_pixel(xx, yy, Color(0, 0, 0, 0)) + return blended_image func _on_Timer_timeout() -> void: Global.can_draw = true \ No newline at end of file diff --git a/Scripts/Global.gd b/Scripts/Global.gd index 7c15d154c..c2a38037d 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -18,28 +18,34 @@ var draw_grid := false var canvases := [] var canvas : Canvas var canvas_parent : Node +var second_viewport : ViewportContainer +var viewport_separator : VSeparator +var split_screen_button : Button # warning-ignore:unused_class_variable var left_square_indicator_visible := true # warning-ignore:unused_class_variable var right_square_indicator_visible := false -# warning-ignore:unused_class_variable -var left_brush_size := 1 -# warning-ignore:unused_class_variable -var right_brush_size := 1 var camera : Camera2D +var camera2 : Camera2D var selection_rectangle : Polygon2D +# warning-ignore:unused_class_variable var selected_pixels := [] var image_clipboard : Image var file_menu : MenuButton var edit_menu : MenuButton var view_menu : MenuButton +var help_menu : MenuButton var left_indicator : Sprite var right_indicator : Sprite var left_color_picker : ColorPickerButton var right_color_picker : ColorPickerButton var left_brush_size_edit : SpinBox var right_brush_size_edit : SpinBox +var left_interpolate_slider : HSlider +var right_interpolate_slider : HSlider + + var loop_animation_button : Button var play_forward : Button var play_backwards : Button @@ -60,12 +66,34 @@ var current_left_tool := "Pencil" # warning-ignore:unused_class_variable var current_right_tool := "Eraser" +#Brushes +enum BRUSH_TYPES {PIXEL, CUSTOM} +# warning-ignore:unused_class_variable +var left_brush_size := 1 +# warning-ignore:unused_class_variable +var right_brush_size := 1 +# warning-ignore:unused_class_variable +var current_left_brush_type = BRUSH_TYPES.PIXEL +# warning-ignore:unused_class_variable +var current_right_brush_type = BRUSH_TYPES.PIXEL +# warning-ignore:unused_class_variable +var custom_brushes := [] +# warning-ignore:unused_class_variable +var custom_left_brush_index := -1 +# warning-ignore:unused_class_variable +var custom_right_brush_index := -1 + + func _ready() -> void: var root = get_tree().get_root() canvas = find_node_by_name(root, "Canvas") canvases.append(canvas) canvas_parent = canvas.get_parent() + second_viewport = find_node_by_name(root, "ViewportContainer2") + viewport_separator = find_node_by_name(root, "ViewportSeparator") + split_screen_button = find_node_by_name(root, "SplitScreenButton") camera = find_node_by_name(canvas_parent, "Camera2D") + camera2 = find_node_by_name(canvas_parent.get_parent().get_parent(), "Camera2D2") selection_rectangle = find_node_by_name(root, "SelectionRectangle") image_clipboard = Image.new() @@ -73,12 +101,15 @@ 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") + help_menu = find_node_by_name(root, "HelpMenu") left_indicator = find_node_by_name(root, "LeftIndicator") right_indicator = find_node_by_name(root, "RightIndicator") left_color_picker = find_node_by_name(root, "LeftColorPickerButton") right_color_picker = find_node_by_name(root, "RightColorPickerButton") left_brush_size_edit = find_node_by_name(root, "LeftBrushSizeEdit") right_brush_size_edit = find_node_by_name(root, "RightBrushSizeEdit") + left_interpolate_slider = find_node_by_name(root, "LeftInterpolateFactor") + right_interpolate_slider = find_node_by_name(root, "RightInterpolateFactor") loop_animation_button = find_node_by_name(root, "LoopAnim") play_forward = find_node_by_name(root, "PlayForward") @@ -133,4 +164,14 @@ func handle_layer_order_buttons() -> void: func set_current_frame_label(value) -> void: current_frame = value - current_frame_label.text = "Current frame: %s/%s" % [str(current_frame + 1), canvases.size()] \ No newline at end of file + current_frame_label.text = "Current frame: %s/%s" % [str(current_frame + 1), canvases.size()] + +func create_brush_button(brush_img : Image) -> void: + var brush_button = load("res://Prefabs/BrushButton.tscn").instance() + brush_button.brush_type = Global.BRUSH_TYPES.CUSTOM + brush_button.custom_brush_index = Global.custom_brushes.size() - 1 + var brush_tex := ImageTexture.new() + brush_tex.create_from_image(brush_img, 0) + brush_button.get_child(0).texture = brush_tex + var hbox_container := find_node_by_name(get_tree().get_root(), "BrushHBoxContainer") + hbox_container.add_child(brush_button) \ No newline at end of file diff --git a/Scripts/LayerContainer.gd b/Scripts/LayerContainer.gd index c565630b6..175d1fa1a 100644 --- a/Scripts/LayerContainer.gd +++ b/Scripts/LayerContainer.gd @@ -14,7 +14,7 @@ func _ready() -> void: # warning-ignore:unused_argument func _process(delta) -> void: var mouse_pos := get_local_mouse_position() + rect_position - if point_in_rectangle(mouse_pos, rect_position, rect_position + rect_size) && !visibility_toggled: + if Rect2(rect_position, rect_position + rect_size).has_point(mouse_pos) && !visibility_toggled: if Input.is_action_just_pressed("left_mouse"): Global.canvas.current_layer_index = i changed_selection() @@ -48,9 +48,6 @@ func changed_selection() -> void: child.currently_selected = false child.get_stylebox("panel").bg_color = Color("3d3b45") -func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool: - return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y - func _on_VisibilityButton_pressed() -> void: if Global.canvas.layers[i][3]: Global.canvas.layers[i][3] = false diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 0d6515f90..ac6b628bd 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -34,6 +34,7 @@ func _ready() -> void: } var edit_menu_items := { "Scale Image" : 0, + "Crop Image" : 0, "Clear Selection" : 0 #"Undo" : KEY_MASK_CTRL + KEY_Z, #"Redo" : KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Z, @@ -41,12 +42,15 @@ func _ready() -> void: var view_menu_items := { "Tile Mode" : KEY_MASK_CTRL + KEY_T, "Show Grid" : KEY_MASK_CTRL + KEY_G - #"Undo" : KEY_MASK_CTRL + KEY_Z, - #"Redo" : KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Z, + } + var help_menu_items := { + "About Pixelorama" : 0 } 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 help_menu : PopupMenu = Global.help_menu.get_popup() + var i = 0 for item in file_menu_items.keys(): file_menu.add_item(item, i, file_menu_items[item]) @@ -59,9 +63,15 @@ func _ready() -> void: for item in view_menu_items.keys(): view_menu.add_check_item(item, i, view_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 + 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") + help_menu.connect("id_pressed", self, "help_menu_id_pressed") var root = get_tree().get_root() pencil_tool = Global.find_node_by_name(root, "Pencil") @@ -149,7 +159,37 @@ func edit_menu_id_pressed(id : int) -> void: 0: #Scale Image $ScaleImage.popup_centered() Global.can_draw = false - 1: #Clear selection + 1: #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 + 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 + 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 used_rect == Rect2(0, 0, 0, 0): + return + + #Loop through all the layers to crop them + for j in range(Global.canvas.layers.size() - 1, -1, -1): + var sprite := Image.new() + sprite = Global.canvas.layers[j][0].get_rect(used_rect) + Global.canvas.layers[j][0] = sprite + Global.canvas.layers[j][0].lock() + Global.canvas.update_texture(j) + + var width = Global.canvas.layers[0][0].get_width() + var height = Global.canvas.layers[0][0].get_height() + Global.canvas.size = Vector2(width, height).floor() + Global.canvas.camera_zoom() + 2: #Clear selection Global.selection_rectangle.polygon[0] = Vector2.ZERO Global.selection_rectangle.polygon[1] = Vector2.ZERO Global.selection_rectangle.polygon[2] = Vector2.ZERO @@ -165,12 +205,18 @@ func view_menu_id_pressed(id : int) -> void: Global.draw_grid = !Global.draw_grid view_menu.set_item_checked(1, Global.draw_grid) +func help_menu_id_pressed(id : int) -> void: + match id: + 0: #About Pixelorama + $AboutDialog.popup_centered() + Global.can_draw = false + func _on_CreateNewImage_confirmed() -> void: var width := float($CreateNewImage/VBoxContainer/WidthCont/WidthValue.value) var height := float($CreateNewImage/VBoxContainer/HeightCont/HeightValue.value) var fill_color : Color = $CreateNewImage/VBoxContainer/FillColor/FillColor.color clear_canvases() - Global.canvas = load("res://Canvas.tscn").instance() + Global.canvas = load("res://Prefabs/Canvas.tscn").instance() Global.canvas.size = Vector2(width, height).floor() Global.canvas_parent.add_child(Global.canvas) @@ -187,12 +233,15 @@ func _on_OpenSprite_file_selected(path) -> void: var file := File.new() var err := file.open(path, File.READ) if err == 0: + var current_version : String = ProjectSettings.get_setting("application/config/Version") var version := file.get_line() + if current_version != version: + OS.alert("File is from an older version of Pixelorama, as such it might not work properly") var frame := 0 var frame_line := file.get_line() clear_canvases() while frame_line == "--": - var canvas : Canvas = load("res://Canvas.tscn").instance() + var canvas : Canvas = load("res://Prefabs/Canvas.tscn").instance() Global.canvas = canvas var width := file.get_16() var height := file.get_16() @@ -233,6 +282,18 @@ func _on_OpenSprite_file_selected(path) -> void: for color in right_palette: Global.right_color_picker.get_picker().add_preset(color) + #Load custom brushes + var brush_line := file.get_line() + while brush_line == "/": + var b_width := file.get_16() + var b_height := file.get_16() + var buffer := file.get_buffer(b_width * b_height * 4) + var image := Image.new() + image.create_from_data(b_width, b_height, false, Image.FORMAT_RGBA8, buffer) + Global.custom_brushes.append(image) + Global.create_brush_button(image) + brush_line = file.get_line() + file.close() func _on_SaveSprite_file_selected(path) -> void: @@ -264,6 +325,13 @@ func _on_SaveSprite_file_selected(path) -> void: file.store_8(right_brush_size) file.store_var(left_palette) file.store_var(right_palette) + #Save custom brushes + for brush in Global.custom_brushes: + file.store_line("/") + file.store_16(brush.get_size().x) + file.store_16(brush.get_size().y) + file.store_buffer(brush.get_data()) + file.store_line("END_BRUSHES") file.close() func _on_ImportSprites_files_selected(paths) -> void: @@ -279,7 +347,7 @@ func _on_ImportSprites_files_selected(paths) -> void: var err = image.load(path) if err == OK: opensprite_file_selected = true - var canvas : Canvas = load("res://Canvas.tscn").instance() + var canvas : Canvas = load("res://Prefabs/Canvas.tscn").instance() canvas.size = image.get_size() image.convert(Image.FORMAT_RGBA8) image.lock() @@ -488,7 +556,7 @@ func _on_RightBrushSizeEdit_value_changed(value) -> void: Global.right_brush_size = new_size func _on_AddFrame_pressed() -> void: - var canvas = load("res://Canvas.tscn").instance() + var canvas = load("res://Prefabs/Canvas.tscn").instance() canvas.size = Global.canvas.size canvas.frame = Global.canvases.size() for canvas in Global.canvases: @@ -524,7 +592,7 @@ func _on_RemoveFrame_pressed() -> void: func _on_CloneFrame_pressed() -> void: - var canvas = load("res://Canvas.tscn").instance() + var canvas = load("res://Prefabs/Canvas.tscn").instance() canvas.size = Global.canvas.size #canvas.layers = Global.canvas.layers.duplicate(true) for layer in Global.canvas.layers: @@ -666,4 +734,14 @@ func _on_FutureOnionSkinning_value_changed(value) -> void: Global.onion_skinning_future_rate = int(value) func _on_BlueRedMode_toggled(button_pressed) -> void: - Global.onion_skinning_blue_red = button_pressed \ No newline at end of file + Global.onion_skinning_blue_red = button_pressed + +func _on_SplitScreenButton_toggled(button_pressed) -> void: + if button_pressed: + Global.split_screen_button.text = ">" + Global.viewport_separator.visible = true + Global.second_viewport.visible = true + else: + Global.split_screen_button.text = "<" + Global.viewport_separator.visible = false + Global.second_viewport.visible = false diff --git a/Scripts/SecondViewport.gd b/Scripts/SecondViewport.gd new file mode 100644 index 000000000..69227358d --- /dev/null +++ b/Scripts/SecondViewport.gd @@ -0,0 +1,13 @@ +extends Viewport + +# Declare member variables here. Examples: +# var a = 2 +# var b = "text" + +# Called when the node enters the scene tree for the first time. +func _ready(): + world_2d = $"../../ViewportContainer/Viewport".world_2d + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass diff --git a/Scripts/SelectionRectangle.gd b/Scripts/SelectionRectangle.gd index 1297b506e..27386214f 100644 --- a/Scripts/SelectionRectangle.gd +++ b/Scripts/SelectionRectangle.gd @@ -91,11 +91,18 @@ func _process(delta) -> void: Global.selected_pixels.clear() for xx in range(start_pos.x, end_pos.x): for yy in range(start_pos.y, end_pos.y): - Global.selected_pixels.append(Vector2(xx, yy)) + Global.selected_pixels.append(Vector2(xx, yy)) #Handle copy if Input.is_action_just_pressed("copy") && Global.selected_pixels.size() > 0: - Global.image_clipboard = layer.get_rect(Rect2(polygon[0], polygon[2])) + Global.image_clipboard = layer.get_rect(Rect2(polygon[0], polygon[2] - polygon[0])) + #And save as custom brush + var brush_img := Image.new() + brush_img = layer.get_rect(Rect2(polygon[0], polygon[2] - polygon[0])) + brush_img = brush_img.get_rect(brush_img.get_used_rect()) #save only the visible pixels + Global.custom_brushes.append(brush_img) + + Global.create_brush_button(brush_img) #Handle paste #if Input.is_action_just_pressed("paste") && Global.selected_pixels.size() > 0 && !is_dragging: diff --git a/Transparent Background.png.import b/Transparent Background.png.import deleted file mode 100644 index c6f1b1d05..000000000 --- a/Transparent Background.png.import +++ /dev/null @@ -1,13 +0,0 @@ -[remap] - -importer="image" -type="Image" -path="res://.import/Transparent Background.png-b5955c8e7e6eaecd8f24a1732239745d.image" - -[deps] - -source_file="res://Transparent Background.png" -dest_files=[ "res://.import/Transparent Background.png-b5955c8e7e6eaecd8f24a1732239745d.image" ] - -[params] - diff --git a/project.godot b/project.godot index 87f54a925..6a53442d5 100644 --- a/project.godot +++ b/project.godot @@ -37,6 +37,7 @@ Global="*res://Scripts/Global.gd" [debug] +gdscript/warnings/narrowing_conversion=false gdscript/warnings/return_value_discarded=false [input]