From 4e4a5263321d1ef2ab3da0eb437b8158fee70853 Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Wed, 25 Sep 2019 22:59:48 +0300 Subject: [PATCH] Custom brushes, crop image, split screen, about menu & asset re-organizing - Added support for custom brushes. When you Ctrl-C a selection, it gets added to the list of custom brushes. Each mouse button can have a different brush, and the user can choose whether their color comes from the brush itself or the selected color in the tool options. They can also be resized based on the selected brush size. - Custom brushes are also being saved on .pxo files. - You can now crop images (per frame). All layers of that frame are taken into account and are affected. - Added split screen support. The user can toggle between single screen and split screen, where a second canvas is being shown. Note that you cannot draw on the second canvas. - Added an About Pixelorama selection on the new Help menu. - Project assets are re-organized. --- ...png-62a2c5eb3e805ff7dbb890edc2b8d883.image | Bin 0 -> 95 bytes ...d.png-62a2c5eb3e805ff7dbb890edc2b8d883.md5 | 3 + ...png-643b5c8878aaf0c84a360796789dae22.image | Bin 98 -> 0 bytes ...l.png-02d24c392cbedb7afeccdbe493729855.md5 | 3 + ....png-02d24c392cbedb7afeccdbe493729855.stex | Bin 0 -> 133 bytes .../Graphics/Transparent Background.png | Bin .../Transparent Background.png.import | 13 + Assets/Graphics/grid.png | Bin 87 -> 0 bytes Assets/Graphics/grid.png.import | 13 - Assets/Graphics/pixel.png | Bin 0 -> 101 bytes Assets/Graphics/pixel.png.import | 34 ++ Main.tscn | 337 +++++++++++++++--- Prefabs/BrushButton.tscn | 22 ++ Canvas.tscn => Prefabs/Canvas.tscn | 0 FrameButton.tscn => Prefabs/FrameButton.tscn | 0 .../LayerContainer.tscn | 0 Scripts/AboutDialog.gd | 19 + Scripts/BrushButton.gd | 14 + Scripts/CameraMovement.gd | 24 +- Scripts/Canvas.gd | 137 ++++--- Scripts/Global.gd | 51 ++- Scripts/LayerContainer.gd | 5 +- Scripts/Main.gd | 96 ++++- Scripts/SecondViewport.gd | 13 + Scripts/SelectionRectangle.gd | 11 +- Transparent Background.png.import | 13 - project.godot | 1 + 27 files changed, 659 insertions(+), 150 deletions(-) create mode 100644 .import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image create mode 100644 .import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.md5 delete mode 100644 .import/grid.png-643b5c8878aaf0c84a360796789dae22.image create mode 100644 .import/pixel.png-02d24c392cbedb7afeccdbe493729855.md5 create mode 100644 .import/pixel.png-02d24c392cbedb7afeccdbe493729855.stex rename Transparent Background.png => Assets/Graphics/Transparent Background.png (100%) create mode 100644 Assets/Graphics/Transparent Background.png.import delete mode 100644 Assets/Graphics/grid.png delete mode 100644 Assets/Graphics/grid.png.import create mode 100644 Assets/Graphics/pixel.png create mode 100644 Assets/Graphics/pixel.png.import create mode 100644 Prefabs/BrushButton.tscn rename Canvas.tscn => Prefabs/Canvas.tscn (100%) rename FrameButton.tscn => Prefabs/FrameButton.tscn (100%) rename LayerContainer.tscn => Prefabs/LayerContainer.tscn (100%) create mode 100644 Scripts/AboutDialog.gd create mode 100644 Scripts/BrushButton.gd create mode 100644 Scripts/SecondViewport.gd delete mode 100644 Transparent Background.png.import diff --git a/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image b/.import/Transparent Background.png-62a2c5eb3e805ff7dbb890edc2b8d883.image new file mode 100644 index 0000000000000000000000000000000000000000..b1356d3e515eed70f4dcfab0728cf842fd82ea45 GIT binary patch literal 95 zcmZ>F@$_Y8U|=Z7OYaQubLZuf;sWw`Jw054fHV^bbFcx)qHVk?KuX%v#W93qChNgQ rMg|5JW`%$M&+O0)HRhI@w3@x?74M!$f_D^w${0Ld{an^LB{Ts5eO4B+ literal 0 HcmV?d00001 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 70deb678d8991b46cb43eee93bf83231e895cfc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmZ>F@$_Y8U|=Z7OYaQubLZuf;sWw`Jw054fHWrvbFcx)2NT%@fRvo4i(`ny)Z~F2@X+UU|;}Y2oPXr1Tr##IKa1|%O$WD@{Va-J@ZAre!QQxXz>oOfVRSn|SN j*l-z7q-zh4ON%hWObr(9mOh0YK$Q%hu6{1-oD!M<{mc}i 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 0000000000000000000000000000000000000000..e0c7ff938c6c9eaa46d6b9c59caa7e58db1c907a GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz9Zwg>kcif|=MHi*FmNy%mj0hx y$}adv<^Qy9hKBT=cfMXt*JVD(A@D(tfr0tb4MslYfH*s#J_b)$KbLh*2~7Y3yByyD literal 0 HcmV?d00001 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]