From 768a1e7b8f1ae6a03ed1176c7c2eac742c6b7c68 Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Sat, 25 Apr 2020 00:42:02 +0300 Subject: [PATCH] Added Patterns for the bucket tool The bucket tool can now use Patterns instead of colors to fill areas. They get loaded from the "Patterns" folder, similar to how Brushes and Palletes work. You can no longer use brushes for the bucket tool. --- Changelog.md | 2 +- Main.tscn | 161 +++++++++++++++++---- Prefabs/PatternButton.gd | 22 +++ Prefabs/PatternButton.tscn | 27 ++++ Scripts/BrushButton.gd | 4 +- Scripts/Canvas.gd | 37 ++--- Scripts/Dialogs/SplashDialog.gd | 2 +- Scripts/Global.gd | 81 +++-------- Scripts/Import.gd | 73 +++++----- Scripts/Main.gd | 35 ++++- Scripts/Palette/PaletteContainer.gd | 3 +- Scripts/XDGDataPaths.gd | 23 ++- pixelorama/Patterns/.gdignore | 0 pixelorama/Patterns/Brick.png | Bin 0 -> 912 bytes pixelorama/Patterns/Gravel.png | Bin 0 -> 387 bytes pixelorama/Patterns/Ground_leaves.png | Bin 0 -> 8450 bytes pixelorama/Patterns/Light_Blue_Texture.png | Bin 0 -> 7956 bytes pixelorama/Patterns/Metro_tile.png | Bin 0 -> 2275 bytes pixelorama/Patterns/Purple_Texture.png | Bin 0 -> 8450 bytes pixelorama/Patterns/White_Marble.png | Bin 0 -> 795 bytes pixelorama/Patterns/Wooden_planks_1.png | Bin 0 -> 6223 bytes pixelorama/Patterns/Yellow_Marble.png | Bin 0 -> 2275 bytes pixelorama/Patterns/small_stonebricks.png | Bin 0 -> 1019 bytes pixelorama/Patterns/wooden_planks_2.png | Bin 0 -> 7417 bytes 24 files changed, 315 insertions(+), 155 deletions(-) create mode 100644 Prefabs/PatternButton.gd create mode 100644 Prefabs/PatternButton.tscn create mode 100644 pixelorama/Patterns/.gdignore create mode 100644 pixelorama/Patterns/Brick.png create mode 100644 pixelorama/Patterns/Gravel.png create mode 100644 pixelorama/Patterns/Ground_leaves.png create mode 100644 pixelorama/Patterns/Light_Blue_Texture.png create mode 100644 pixelorama/Patterns/Metro_tile.png create mode 100644 pixelorama/Patterns/Purple_Texture.png create mode 100644 pixelorama/Patterns/White_Marble.png create mode 100644 pixelorama/Patterns/Wooden_planks_1.png create mode 100644 pixelorama/Patterns/Yellow_Marble.png create mode 100644 pixelorama/Patterns/small_stonebricks.png create mode 100644 pixelorama/Patterns/wooden_planks_2.png diff --git a/Changelog.md b/Changelog.md index ba40d3064..ab805190b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,7 +15,7 @@ Martin Novák (novhack), Darshan Phaldesai (luiq54), Schweini07, Marco Galli (Ga - You can now export your projects to .gif files. - A new rotation method has been added, "Upscale, Rotate and Downscale". It's similar to Rotsprite. - An HSV Adjust dialog has been added in the Images menu. -- Pattern filling is now possible. If the user chooses a brush that is not the pixel or a circle brush and uses the bucket tool, the brush image is used as a pattern that fills the area. +- Pattern filling is now possible. The bucket tool can now use Patterns to fill areas, instead of a single color. - Users can now change keyboard shortcut bindings for tools, in the Preferences. - Importing .pngs as palettes is now possible. - A confirmation message now appears when the user quits Pixelorama, if there are unsaved changes. diff --git a/Main.tscn b/Main.tscn index 62044dc09..400c1fd61 100644 --- a/Main.tscn +++ b/Main.tscn @@ -677,13 +677,13 @@ ticks_on_borders = true [node name="LeftFillArea" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions"] visible = false -margin_top = 75.0 -margin_right = 159.0 -margin_bottom = 117.0 +margin_top = 71.0 +margin_right = 160.0 +margin_bottom = 155.0 [node name="FillAreaLabel" type="Label" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea"] -margin_left = 56.0 -margin_right = 102.0 +margin_left = 57.0 +margin_right = 103.0 margin_bottom = 15.0 mouse_default_cursor_shape = 2 size_flags_horizontal = 4 @@ -692,8 +692,8 @@ text = "Fill area:" [node name="LeftFillAreaOptions" type="OptionButton" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea"] margin_left = 24.0 margin_top = 19.0 -margin_right = 134.0 -margin_bottom = 42.0 +margin_right = 136.0 +margin_bottom = 40.0 focus_mode = 0 mouse_default_cursor_shape = 2 size_flags_horizontal = 4 @@ -701,6 +701,50 @@ text = "Same color area" items = [ "Same color area", null, false, 0, null, "Same color pixels", null, false, 1, null ] selected = 0 +[node name="FillWithLabel" type="Label" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea"] +margin_left = 57.0 +margin_top = 44.0 +margin_right = 102.0 +margin_bottom = 59.0 +size_flags_horizontal = 4 +text = "Fill with:" + +[node name="LeftFillWithOptions" type="OptionButton" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea"] +margin_left = 42.0 +margin_top = 63.0 +margin_right = 118.0 +margin_bottom = 84.0 +mouse_default_cursor_shape = 2 +size_flags_horizontal = 4 +text = "Selected Color" +items = [ "Selected Color", null, false, 0, null, "Pattern", null, false, 1, null ] +selected = 0 + +[node name="LeftFillPattern" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea"] +visible = false +margin_left = 59.0 +margin_top = 88.0 +margin_right = 95.0 +margin_bottom = 120.0 +size_flags_horizontal = 4 + +[node name="LeftPatternTypeButton" type="TextureButton" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea/LeftFillPattern"] +margin_right = 36.0 +margin_bottom = 32.0 +rect_min_size = Vector2( 36, 32 ) +hint_tooltip = "Select a brush" +mouse_default_cursor_shape = 2 +texture_normal = ExtResource( 12 ) + +[node name="PatternTexture" type="TextureRect" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea/LeftFillPattern/LeftPatternTypeButton"] +margin_right = 32.0 +margin_bottom = 32.0 +expand = true +stretch_mode = 6 +__meta__ = { +"_edit_use_anchors_": false +} + [node name="LeftLDOptions" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions"] visible = false margin_top = 75.0 @@ -987,22 +1031,22 @@ ticks_on_borders = true [node name="RightFillArea" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions"] visible = false -margin_top = 75.0 -margin_right = 144.0 -margin_bottom = 117.0 +margin_top = 71.0 +margin_right = 160.0 +margin_bottom = 155.0 [node name="FillAreaLabel" type="Label" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea"] -margin_left = 49.0 -margin_right = 95.0 +margin_left = 57.0 +margin_right = 103.0 margin_bottom = 15.0 size_flags_horizontal = 4 text = "Fill area:" [node name="RightFillAreaOptions" type="OptionButton" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea"] -margin_left = 17.0 +margin_left = 24.0 margin_top = 19.0 -margin_right = 127.0 -margin_bottom = 42.0 +margin_right = 136.0 +margin_bottom = 40.0 focus_mode = 0 mouse_default_cursor_shape = 2 size_flags_horizontal = 4 @@ -1010,6 +1054,50 @@ text = "Same color area" items = [ "Same color area", null, false, 0, null, "Same color pixels", null, false, 1, null ] selected = 0 +[node name="FillWithLabel" type="Label" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea"] +margin_left = 57.0 +margin_top = 44.0 +margin_right = 102.0 +margin_bottom = 59.0 +size_flags_horizontal = 4 +text = "Fill with:" + +[node name="RightFillWithOptions" type="OptionButton" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea"] +margin_left = 38.0 +margin_top = 63.0 +margin_right = 122.0 +margin_bottom = 84.0 +mouse_default_cursor_shape = 2 +size_flags_horizontal = 4 +text = "Selected Color" +items = [ "Selected Color", null, false, 0, null, "Pattern", null, false, 1, null ] +selected = 0 + +[node name="RightFillPattern" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea"] +visible = false +margin_left = 59.0 +margin_top = 88.0 +margin_right = 95.0 +margin_bottom = 120.0 +size_flags_horizontal = 4 + +[node name="RightPatternTypeButton" type="TextureButton" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea/RightFillPattern"] +margin_right = 36.0 +margin_bottom = 32.0 +rect_min_size = Vector2( 36, 32 ) +hint_tooltip = "Select a brush" +mouse_default_cursor_shape = 2 +texture_normal = ExtResource( 12 ) + +[node name="PatternTexture" type="TextureRect" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea/RightFillPattern/RightPatternTypeButton"] +margin_right = 32.0 +margin_bottom = 32.0 +expand = true +stretch_mode = 6 +__meta__ = { +"_edit_use_anchors_": false +} + [node name="RightLDOptions" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions"] visible = false margin_top = 75.0 @@ -1310,11 +1398,13 @@ columns = 6 [node name="PixelBrushButton" parent="BrushesPopup/TabContainer/File/FileBrushContainer" instance=ExtResource( 26 )] hint_tooltip = "Pixel brush" +mouse_default_cursor_shape = 2 [node name="CircleBrushButton" parent="BrushesPopup/TabContainer/File/FileBrushContainer" instance=ExtResource( 26 )] -margin_left = 32.0 -margin_right = 64.0 +margin_left = 35.0 +margin_right = 67.0 hint_tooltip = "Filled circle brush" +mouse_default_cursor_shape = 2 brush_type = 1 custom_brush_index = -2 @@ -1322,9 +1412,10 @@ custom_brush_index = -2 texture = SubResource( 9 ) [node name="FilledCircleBrushButton" parent="BrushesPopup/TabContainer/File/FileBrushContainer" instance=ExtResource( 26 )] -margin_left = 64.0 -margin_right = 96.0 +margin_left = 70.0 +margin_right = 102.0 hint_tooltip = "Circle brush" +mouse_default_cursor_shape = 2 brush_type = 2 custom_brush_index = -1 @@ -1346,6 +1437,23 @@ scroll_horizontal_enabled = false [node name="ProjectBrushContainer" type="GridContainer" parent="BrushesPopup/TabContainer/Project"] columns = 5 +[node name="PatternsPopup" type="PopupPanel" parent="."] +margin_right = 226.0 +margin_bottom = 104.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ScrollContainer" type="ScrollContainer" parent="PatternsPopup"] +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_min_size = Vector2( 0, 36 ) +size_flags_horizontal = 3 +scroll_horizontal_enabled = false + +[node name="PatternContainer" type="GridContainer" parent="PatternsPopup/ScrollContainer"] +columns = 6 + [node name="SplashDialog" parent="." instance=ExtResource( 27 )] [node name="CreateNewImage" parent="." instance=ExtResource( 28 )] @@ -1358,8 +1466,8 @@ resizable = true mode = 0 access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "E:/Projekty/Godot/Pixelorama" -current_path = "E:/Projekty/Godot/Pixelorama/" +current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" [node name="SaveSprite" type="FileDialog" parent="."] anchor_left = 0.5 @@ -1374,13 +1482,11 @@ window_title = "Save Sprite as .pxo" resizable = true access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "E:/Projekty/Godot/Pixelorama" -current_path = "E:/Projekty/Godot/Pixelorama/" +current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" [node name="ImportSprites" parent="." instance=ExtResource( 29 )] window_title = "Otwórz plik(i)" -current_dir = "E:/Projekty/Godot/Pixelorama" -current_path = "E:/Projekty/Godot/Pixelorama/" [node name="ExportDialog" parent="." instance=ExtResource( 39 )] @@ -1430,7 +1536,6 @@ dialog_text = "This is an error message!" [node name="PaletteImportFileDialog" parent="." instance=ExtResource( 37 )] window_title = "Otwórz plik" filters = PoolStringArray( "*.json ; JavaScript Object Notation", "*.gpl ; Gimp Palette Library", "*.png; Portable Network Graphics" ) -current_file = "Pixelorama" current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects" current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/" @@ -1470,6 +1575,8 @@ visible = false [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftColorInterpolation/LeftInterpolateFactor" to="." method="_on_LeftInterpolateFactor_value_changed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftColorInterpolation/LeftInterpolateSlider" to="." method="_on_LeftInterpolateFactor_value_changed"] [connection signal="item_selected" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea/LeftFillAreaOptions" to="." method="_on_LeftFillAreaOptions_item_selected"] +[connection signal="item_selected" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea/LeftFillWithOptions" to="." method="_on_LeftFillWithOptions_item_selected"] +[connection signal="pressed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftFillArea/LeftFillPattern/LeftPatternTypeButton" to="." method="_on_LeftPatternTypeButton_pressed"] [connection signal="item_selected" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftLDOptions/LeftLightenDarken" to="." method="_on_LeftLightenDarken_item_selected"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftLDOptions/LeftLDAmountSpinbox" to="." method="_on_LeftLDAmountSpinbox_value_changed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftLDOptions/LeftLDAmountSlider" to="." method="_on_LeftLDAmountSpinbox_value_changed"] @@ -1485,6 +1592,8 @@ visible = false [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightColorInterpolation/RightInterpolateFactor" to="." method="_on_RightInterpolateFactor_value_changed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightColorInterpolation/RightInterpolateSlider" to="." method="_on_RightInterpolateFactor_value_changed"] [connection signal="item_selected" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea/RightFillAreaOptions" to="." method="_on_RightFillAreaOptions_item_selected"] +[connection signal="item_selected" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea/RightFillWithOptions" to="." method="_on_RightFillWithOptions_item_selected"] +[connection signal="pressed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightFillArea/RightFillPattern/RightPatternTypeButton" to="." method="_on_RightPatternTypeButton_pressed"] [connection signal="item_selected" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightLDOptions/RightLightenDarken" to="." method="_on_RightLightenDarken_item_selected"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightLDOptions/RightLDAmountSpinbox" to="." method="_on_RightLDAmountSpinbox_value_changed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightLDOptions/RightLDAmountSlider" to="." method="_on_RightLDAmountSpinbox_value_changed"] diff --git a/Prefabs/PatternButton.gd b/Prefabs/PatternButton.gd new file mode 100644 index 000000000..6e45b4b55 --- /dev/null +++ b/Prefabs/PatternButton.gd @@ -0,0 +1,22 @@ +extends TextureButton + + +var image : Image +var texture : ImageTexture + + +func _ready(): + if image: + texture = ImageTexture.new() + texture.create_from_image(image, 0) + + +func _on_PatternButton_pressed() -> void: + if Global.pattern_window_position == "left": + Global.pattern_left_image = image + Global.left_fill_pattern_container.get_child(0).get_child(0).texture = texture + + elif Global.pattern_window_position == "right": + Global.pattern_right_image = image + Global.right_fill_pattern_container.get_child(0).get_child(0).texture = texture + Global.patterns_popup.hide() diff --git a/Prefabs/PatternButton.tscn b/Prefabs/PatternButton.tscn new file mode 100644 index 000000000..8f2e631f7 --- /dev/null +++ b/Prefabs/PatternButton.tscn @@ -0,0 +1,27 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://Assets/Graphics/Brush_button.png" type="Texture" id=1] +[ext_resource path="res://Prefabs/PatternButton.gd" type="Script" id=2] + +[node name="PatternButton" type="TextureButton"] +margin_right = 32.0 +margin_bottom = 32.0 +rect_min_size = Vector2( 3, 0 ) +button_mask = 7 +texture_normal = ExtResource( 1 ) +stretch_mode = 5 +script = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="PatternTexture" type="TextureRect" parent="."] +margin_right = 32.0 +margin_bottom = 32.0 +rect_min_size = Vector2( 32, 32 ) +expand = true +stretch_mode = 6 +__meta__ = { +"_edit_use_anchors_": false +} +[connection signal="pressed" from="." to="." method="_on_PatternButton_pressed"] diff --git a/Scripts/BrushButton.gd b/Scripts/BrushButton.gd index 8c50be0d0..cfe5db4b4 100644 --- a/Scripts/BrushButton.gd +++ b/Scripts/BrushButton.gd @@ -17,7 +17,7 @@ func _on_BrushButton_pressed() -> void: Global.current_left_brush_type = brush_type Global.custom_left_brush_index = custom_brush_index if custom_brush_index > -1: # Custom brush - if Global.current_left_tool == "Pencil" or Global.current_left_tool == "Bucket": + if Global.current_left_tool == "Pencil": Global.left_color_interpolation_container.visible = true # if hint_tooltip == "": # Global.left_brush_type_label.text = tr("Custom brush") @@ -40,7 +40,7 @@ func _on_BrushButton_pressed() -> void: Global.current_right_brush_type = brush_type Global.custom_right_brush_index = custom_brush_index if custom_brush_index > -1: - if Global.current_right_tool == "Pencil" or Global.current_right_tool == "Bucket": + if Global.current_right_tool == "Pencil": Global.right_color_interpolation_container.visible = true # if hint_tooltip == "": # Global.right_brush_type_label.text = tr("Custom brush") diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index caea3965b..eaaeedefc 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -338,19 +338,14 @@ func _input(event : InputEvent) -> void: pencil_and_eraser(sprite, mouse_pos, Color(0, 0, 0, 0), current_mouse_button, current_action) "Bucket": if can_handle: - var brush_type = Global.Brush_Types.PIXEL - var custom_brush_image : Image - var is_custom_brush := false + var fill_with := 0 + var pattern_image : Image if current_mouse_button == "left_mouse": - brush_type = Global.current_left_brush_type - is_custom_brush = brush_type == Global.Brush_Types.FILE or brush_type == Global.Brush_Types.CUSTOM or brush_type == Global.Brush_Types.RANDOM_FILE - if is_custom_brush: - custom_brush_image = Global.custom_left_brush_image + fill_with = Global.left_fill_with + pattern_image = Global.pattern_left_image elif current_mouse_button == "right_mouse": - brush_type = Global.current_right_brush_type - is_custom_brush = brush_type == Global.Brush_Types.FILE or brush_type == Global.Brush_Types.CUSTOM or brush_type == Global.Brush_Types.RANDOM_FILE - if is_custom_brush: - custom_brush_image = Global.custom_right_brush_image + fill_with = Global.right_fill_with + pattern_image = Global.pattern_right_image if fill_area == 0: # Paint the specific area of the same color var horizontal_mirror := false @@ -364,17 +359,17 @@ func _input(event : InputEvent) -> void: horizontal_mirror = Global.right_horizontal_mirror vertical_mirror = Global.right_vertical_mirror - if is_custom_brush: # Pattern fill - pattern_fill(sprite, mouse_pos, custom_brush_image, sprite.get_pixelv(mouse_pos)) + if fill_with == 1: # Pattern fill + pattern_fill(sprite, mouse_pos, pattern_image, sprite.get_pixelv(mouse_pos)) if horizontal_mirror: var pos := Vector2(mirror_x, mouse_pos.y) - pattern_fill(sprite, pos, custom_brush_image, sprite.get_pixelv(mouse_pos)) + pattern_fill(sprite, pos, pattern_image, sprite.get_pixelv(mouse_pos)) if vertical_mirror: var pos := Vector2(mouse_pos.x, mirror_y) - pattern_fill(sprite, pos, custom_brush_image, sprite.get_pixelv(mouse_pos)) + pattern_fill(sprite, pos, pattern_image, sprite.get_pixelv(mouse_pos)) if horizontal_mirror && vertical_mirror: var pos := Vector2(mirror_x, mirror_y) - pattern_fill(sprite, pos, custom_brush_image, sprite.get_pixelv(mouse_pos)) + pattern_fill(sprite, pos, pattern_image, sprite.get_pixelv(mouse_pos)) else: # Flood fill flood_fill(sprite, mouse_pos, sprite.get_pixelv(mouse_pos), current_color) if horizontal_mirror: @@ -393,14 +388,14 @@ func _input(event : InputEvent) -> void: for yy in range(north_limit, south_limit): var c : Color = sprite.get_pixel(xx, yy) if c == pixel_color: - if is_custom_brush: # Pattern fill - custom_brush_image.lock() - var pattern_size := custom_brush_image.get_size() + if fill_with == 1: # Pattern fill + pattern_image.lock() + var pattern_size := pattern_image.get_size() var xxx : int = int(xx) % int(pattern_size.x) var yyy : int = int(yy) % int(pattern_size.y) - var pattern_color : Color = custom_brush_image.get_pixel(xxx, yyy) + var pattern_color : Color = pattern_image.get_pixel(xxx, yyy) sprite.set_pixel(xx, yy, pattern_color) - custom_brush_image.unlock() + pattern_image.unlock() else: sprite.set_pixel(xx, yy, current_color) sprite_changed_this_frame = true diff --git a/Scripts/Dialogs/SplashDialog.gd b/Scripts/Dialogs/SplashDialog.gd index 37099130e..a2d2063b7 100644 --- a/Scripts/Dialogs/SplashDialog.gd +++ b/Scripts/Dialogs/SplashDialog.gd @@ -13,7 +13,7 @@ func _on_SplashDialog_about_to_show() -> void: var current_version : String = ProjectSettings.get_setting("application/config/Version") window_title = "Pixelorama" + " " + current_version changes_label.text = current_version + " " + tr("Changes") - developed_by_label.text = "Pixelorama" + " " + current_version + " - " + tr("MADEBY_LABEL") + developed_by_label.text = "Pixelorama" + " " + current_version + " - " + tr("Developed by Orama Interactive") art_by_label.text = tr("Art by") + ": Erevos" if "zh" in TranslationServer.get_locale(): diff --git a/Scripts/Global.gd b/Scripts/Global.gd index f23558f67..a26cba4b8 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -27,8 +27,6 @@ enum Direction { # that direction has been pressed. var key_move_press_time := [0.0, 0.0, 0.0, 0.0] - -# warning-ignore:unused_class_variable var loaded_locales : Array var undo_redo : UndoRedo var undos := 0 # The number of times we added undo properties @@ -40,9 +38,9 @@ var layers := [] setget layers_changed var layers_changed_skip := false var current_frame := 0 setget frame_changed var current_layer := 0 setget layer_changed -# warning-ignore:unused_class_variable + var can_draw := false -# warning-ignore:unused_class_variable + var has_focus := false var pressure_sensitivity_mode = Pressure_Sensitivity.NONE var open_last_project := true @@ -50,139 +48,99 @@ var smooth_zoom := true var cursor_image = preload("res://Assets/Graphics/Cursor.png") var left_cursor_tool_texture : ImageTexture var right_cursor_tool_texture : ImageTexture -# warning-ignore:unused_class_variable + var selected_pixels := [] var image_clipboard : Image var animation_tags := [] setget animation_tags_changed # [Name, Color, From, To] var play_only_tags := true -# warning-ignore:unused_class_variable var theme_type := "Dark" -# warning-ignore:unused_class_variable var is_default_image := true -# warning-ignore:unused_class_variable var default_image_width := 64 -# warning-ignore:unused_class_variable var default_image_height := 64 -# warning-ignore:unused_class_variable var default_fill_color := Color(0, 0, 0, 0) var grid_type = Grid_Types.CARTESIAN -# warning-ignore:unused_class_variable var grid_width := 1 -# warning-ignore:unused_class_variable var grid_height := 1 -# warning-ignore:unused_class_variable var grid_color := Color.black -# warning-ignore:unused_class_variable var guide_color := Color.purple -# warning-ignore:unused_class_variable var checker_size := 10 -# warning-ignore:unused_class_variable var checker_color_1 := Color.gray -# warning-ignore:unused_class_variable var checker_color_2 := Color.white # Tools & options -# warning-ignore:unused_class_variable var current_left_tool := "Pencil" -# warning-ignore:unused_class_variable var current_right_tool := "Eraser" -# warning-ignore:unused_class_variable var show_left_tool_icon := true -# warning-ignore:unused_class_variable var show_right_tool_icon := true -# warning-ignore:unused_class_variable var left_square_indicator_visible := true -# warning-ignore:unused_class_variable var right_square_indicator_visible := false -#0 for area of same color, 1 for all pixels of the same color -# warning-ignore:unused_class_variable + +# 0 for area of same color, 1 for all pixels of the same color var left_fill_area := 0 -# warning-ignore:unused_class_variable var right_fill_area := 0 +var left_fill_with := 0 +var right_fill_with := 0 + # 0 for lighten, 1 for darken -# warning-ignore:unused_class_variable var left_ld := 0 -# warning-ignore:unused_class_variable var right_ld := 0 -# warning-ignore:unused_class_variable var left_ld_amount := 0.1 -# warning-ignore:unused_class_variable var right_ld_amount := 0.1 # 0 for the left, 1 for the right -# warning-ignore:unused_class_variable var left_color_picker_for := 0 -# warning-ignore:unused_class_variable var right_color_picker_for := 1 # 0 for zoom in, 1 for zoom out var left_zoom_mode := 0 var right_zoom_mode := 1 -# warning-ignore:unused_class_variable var left_horizontal_mirror := false -# warning-ignore:unused_class_variable var left_vertical_mirror := false -# warning-ignore:unused_class_variable var right_horizontal_mirror := false -# warning-ignore:unused_class_variable var right_vertical_mirror := false # View menu options -# warning-ignore:unused_class_variable var tile_mode := false -# warning-ignore:unused_class_variable var draw_grid := false -# warning-ignore:unused_class_variable var show_rulers := true -# warning-ignore:unused_class_variable var show_guides := true -# warning-ignore:unused_class_variable var show_animation_timeline := true # Onion skinning options var onion_skinning := false -# warning-ignore:unused_class_variable var onion_skinning_past_rate := 1 -# warning-ignore:unused_class_variable var onion_skinning_future_rate := 1 -# warning-ignore:unused_class_variable var onion_skinning_blue_red := false # Brushes -# 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 brush_type_window_position := "left" var left_circle_points := [] var right_circle_points := [] var brushes_from_files := 0 -# 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 -# warning-ignore:unused_class_variable var custom_left_brush_image : Image -# warning-ignore:unused_class_variable var custom_right_brush_image : Image -# warning-ignore:unused_class_variable var custom_left_brush_texture := ImageTexture.new() -# warning-ignore:unused_class_variable var custom_right_brush_texture := ImageTexture.new() +# Patterns +var patterns := [] +var pattern_window_position := "left" +var pattern_left_image : Image +var pattern_right_image : Image + # Palettes -# warning-ignore:unused_class_variable var palettes := {} # Nodes @@ -227,6 +185,7 @@ var right_brush_type_button : BaseButton var brushes_popup : Popup var file_brush_container : GridContainer var project_brush_container : GridContainer +var patterns_popup : Popup var left_brush_size_edit : SpinBox var left_brush_size_slider : HSlider @@ -241,7 +200,9 @@ var right_interpolate_spinbox : SpinBox var right_interpolate_slider : HSlider var left_fill_area_container : Container +var left_fill_pattern_container : Container var right_fill_area_container : Container +var right_fill_pattern_container : Container var left_ld_container : Container var left_ld_amount_slider : HSlider @@ -292,6 +253,7 @@ var palette_import_file_dialog : FileDialog var error_dialog : AcceptDialog + func _ready() -> void: randomize() if OS.has_feature("standalone"): @@ -352,6 +314,7 @@ func _ready() -> void: brushes_popup = find_node_by_name(root, "BrushesPopup") file_brush_container = find_node_by_name(brushes_popup, "FileBrushContainer") project_brush_container = find_node_by_name(brushes_popup, "ProjectBrushContainer") + patterns_popup = find_node_by_name(root, "PatternsPopup") left_brush_size_edit = find_node_by_name(root, "LeftBrushSizeEdit") left_brush_size_slider = find_node_by_name(root, "LeftBrushSizeSlider") @@ -366,7 +329,9 @@ func _ready() -> void: right_interpolate_slider = find_node_by_name(root, "RightInterpolateSlider") left_fill_area_container = find_node_by_name(root, "LeftFillArea") + left_fill_pattern_container = find_node_by_name(root, "LeftFillPattern") right_fill_area_container = find_node_by_name(root, "RightFillArea") + right_fill_pattern_container = find_node_by_name(root, "RightFillPattern") left_ld_container = find_node_by_name(root, "LeftLDOptions") left_ld_amount_slider = find_node_by_name(root, "LeftLDAmountSlider") @@ -838,11 +803,13 @@ func create_brush_button(brush_img : Image, brush_type := Brush_Types.CUSTOM, hi brush_tex.create_from_image(brush_img, 0) brush_button.get_child(0).texture = brush_tex brush_button.hint_tooltip = hint_tooltip + brush_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND brush_button.connect("brush_selected",control,"_on_Brush_Selected") if brush_type == Brush_Types.RANDOM_FILE: brush_button.random_brushes.append(brush_img) brush_container.add_child(brush_button) + func remove_brush_buttons() -> void: current_left_brush_type = Brush_Types.PIXEL current_right_brush_type = Brush_Types.PIXEL diff --git a/Scripts/Import.gd b/Scripts/Import.gd index 336cee1dd..243225101 100644 --- a/Scripts/Import.gd +++ b/Scripts/Import.gd @@ -32,7 +32,6 @@ func get_brush_files_from_directory(directory: String): # -> Array if err != OK: return null - # Build first the list of base png files and all subdirectories to # scan later (skip navigational . and ..) main_directory.list_dir_begin(true) @@ -40,9 +39,8 @@ func get_brush_files_from_directory(directory: String): # -> Array while fname != "": if main_directory.current_is_dir(): subdirectories.append(fname) - else: # Filter for pngs + else: # Filter for pngs if fname.get_extension().to_lower() == "png": - print_debug("Found brush at '%s'" % [fname]) base_png_files.append(fname) # go to next @@ -86,8 +84,7 @@ func get_brush_files_from_directory(directory: String): # -> Array return [base_png_files, randomised_subdir_files_map, nonrandomised_subdir_files_map] -# Add a randomised brush from the given list of files as a -# source. +# Add a randomised brush from the given list of files as a source. # The tooltip name is what shows up on the tooltip # and is probably in this case the name of the containing # randomised directory. @@ -100,7 +97,6 @@ func add_randomised_brush(fpaths : Array, tooltip_name : String) -> void: if err == OK: image.convert(Image.FORMAT_RGBA8) loaded_images.append(image) - print_debug("Loaded image from '%s'" % [filen]) # If any images were successfully loaded, then # we create the randomised brush button, copied @@ -220,40 +216,45 @@ func import_brushes(priority_ordered_search_path: Array) -> void: Global.brushes_from_files = Global.custom_brushes.size() +func import_patterns(priority_ordered_search_path: Array) -> void: + for path in priority_ordered_search_path: + var pattern_list := [] + var dir := Directory.new() + dir.open(path) + dir.list_dir_begin() + var curr_file := dir.get_next() + while curr_file != "": + if curr_file.get_extension().to_lower() == "png": + pattern_list.append(curr_file) + curr_file = dir.get_next() + dir.list_dir_end() -func find_brushes(brushes_dir : Directory, path : String) -> Array: - var subdirectories := [] - var found_random_brush := 0 - path = Global.root_directory.plus_file(path) - brushes_dir.open(path) - brushes_dir.list_dir_begin(true) - var file := brushes_dir.get_next() - while file != "": - if file.get_extension().to_upper() == "PNG": + for pattern in pattern_list: var image := Image.new() - var err := image.load(path.plus_file(file)) + var err := image.load(path.plus_file(pattern)) if err == OK: - if "%" in file: - if found_random_brush == 0: - found_random_brush = Global.file_brush_container.get_child_count() - image.convert(Image.FORMAT_RGBA8) - Global.custom_brushes.append(image) - Global.create_brush_button(image, Global.Brush_Types.RANDOM_FILE, file.trim_suffix(".png")) - else: - var brush_button = Global.file_brush_container.get_child(found_random_brush) - brush_button.random_brushes.append(image) - else: - image.convert(Image.FORMAT_RGBA8) - Global.custom_brushes.append(image) - Global.create_brush_button(image, Global.Brush_Types.FILE, file.trim_suffix(".png")) - elif file.get_extension() == "": # Probably a directory - var subdir := "./%s" % [file] - if brushes_dir.dir_exists(subdir): # If it's an actual directory - subdirectories.append(subdir) + image.convert(Image.FORMAT_RGBA8) + Global.patterns.append(image) + + var pattern_button : BaseButton = load("res://Prefabs/PatternButton.tscn").instance() + pattern_button.image = image + var pattern_tex := ImageTexture.new() + pattern_tex.create_from_image(image, 0) + pattern_button.get_child(0).texture = pattern_tex + pattern_button.hint_tooltip = pattern + pattern_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + Global.patterns_popup.get_node("ScrollContainer/PatternContainer").add_child(pattern_button) + + Global.pattern_left_image = Global.patterns[0] + var pattern_left_tex := ImageTexture.new() + pattern_left_tex.create_from_image(Global.pattern_left_image, 0) + Global.left_fill_pattern_container.get_child(0).get_child(0).texture = pattern_left_tex + + Global.pattern_right_image = Global.patterns[0] + var pattern_right_tex := ImageTexture.new() + pattern_right_tex.create_from_image(Global.pattern_right_image, 0) + Global.right_fill_pattern_container.get_child(0).get_child(0).texture = pattern_right_tex - file = brushes_dir.get_next() - brushes_dir.list_dir_end() - return subdirectories func import_gpl(path : String) -> Palette: var result : Palette = null diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 3fa654dc8..5fbcdba61 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -172,6 +172,7 @@ func _ready() -> void: Global.layers_container.get_child(0).line_edit.text = Global.layers[0][0] Import.import_brushes(Global.directory_module.get_brushes_search_path_in_order()) + Import.import_patterns(Global.directory_module.get_patterns_search_path_in_order()) Global.left_color_picker.get_picker().presets_visible = false Global.right_color_picker.get_picker().presets_visible = false @@ -502,12 +503,8 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le Global.left_brush_size_slider.visible = true Global.left_mirror_container.visible = true elif current_action == "Bucket": - Global.left_brush_type_container.visible = true - Global.left_brush_size_slider.visible = true Global.left_fill_area_container.visible = true Global.left_mirror_container.visible = true - if Global.current_left_brush_type == Global.Brush_Types.FILE or Global.current_left_brush_type == Global.Brush_Types.CUSTOM or Global.current_left_brush_type == Global.Brush_Types.RANDOM_FILE: - Global.left_color_interpolation_container.visible = true elif current_action == "LightenDarken": Global.left_brush_type_container.visible = true Global.left_brush_size_slider.visible = true @@ -538,12 +535,8 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le Global.right_brush_size_slider.visible = true Global.right_mirror_container.visible = true elif current_action == "Bucket": - Global.right_brush_type_container.visible = true - Global.right_brush_size_slider.visible = true Global.right_fill_area_container.visible = true Global.right_mirror_container.visible = true - if Global.current_right_brush_type == Global.Brush_Types.FILE or Global.current_right_brush_type == Global.Brush_Types.CUSTOM or Global.current_right_brush_type == Global.Brush_Types.RANDOM_FILE: - Global.right_color_interpolation_container.visible = true elif current_action == "LightenDarken": Global.right_brush_type_container.visible = true Global.right_brush_size_slider.visible = true @@ -651,10 +644,36 @@ func _on_LeftFillAreaOptions_item_selected(ID : int) -> void: Global.left_fill_area = ID +func _on_LeftFillWithOptions_item_selected(ID : int) -> void: + Global.left_fill_with = ID + if ID == 1: + Global.left_fill_pattern_container.visible = true + else: + Global.left_fill_pattern_container.visible = false + + +func _on_LeftPatternTypeButton_pressed() -> void: + Global.pattern_window_position = "left" + Global.patterns_popup.popup(Rect2(Global.left_brush_type_button.rect_global_position, Vector2(226, 72))) + + func _on_RightFillAreaOptions_item_selected(ID : int) -> void: Global.right_fill_area = ID +func _on_RightFillWithOptions_item_selected(ID : int) -> void: + Global.right_fill_with = ID + if ID == 1: + Global.right_fill_pattern_container.visible = true + else: + Global.right_fill_pattern_container.visible = false + + +func _on_RightPatternTypeButton_pressed() -> void: + Global.pattern_window_position = "right" + Global.patterns_popup.popup(Rect2(Global.right_brush_type_button.rect_global_position, Vector2(226, 72))) + + func _on_LeftLightenDarken_item_selected(ID : int) -> void: Global.left_ld = ID diff --git a/Scripts/Palette/PaletteContainer.gd b/Scripts/Palette/PaletteContainer.gd index 44b264fa7..03166f118 100644 --- a/Scripts/Palette/PaletteContainer.gd +++ b/Scripts/Palette/PaletteContainer.gd @@ -2,11 +2,10 @@ extends GridContainer const palette_button = preload("res://Prefabs/PaletteButton.tscn") - -var palettes_path := "Palettes" var current_palette = "Default" var from_palette : Palette + func _ready() -> void: _load_palettes() diff --git a/Scripts/XDGDataPaths.gd b/Scripts/XDGDataPaths.gd index fe2b5026d..2fa8da356 100644 --- a/Scripts/XDGDataPaths.gd +++ b/Scripts/XDGDataPaths.gd @@ -16,6 +16,7 @@ const config_subdir_name := "pixelorama" const palettes_data_subdirectory := "Palettes" const brushes_data_subdirectory := "Brushes" +const patterns_data_subdirectory := "Patterns" # Get if we should use XDG standard or not nyaaaa @@ -69,7 +70,6 @@ func _init(): xdg_data_dirs = [] - func append_file_to_all(basepaths: Array, subpath: String) -> Array: var res := [] for _path in basepaths: @@ -81,27 +81,44 @@ func append_file_to_all(basepaths: Array, subpath: String) -> Array: func get_search_paths_in_order() -> Array: return [xdg_data_home] + xdg_data_dirs + # Gets the paths, in order of search priority, for palettes. func get_palette_search_path_in_order() -> Array: var base_paths := get_search_paths_in_order() return append_file_to_all(base_paths, palettes_data_subdirectory) + # Gets the paths, in order of search priority, for brushes. func get_brushes_search_path_in_order() -> Array: var base_paths := get_search_paths_in_order() return append_file_to_all(base_paths, brushes_data_subdirectory) +# Gets the paths, in order of search priority, for patterns. +func get_patterns_search_path_in_order() -> Array: + var base_paths := get_search_paths_in_order() + return append_file_to_all(base_paths, patterns_data_subdirectory) + + # Get the path that we are ok to be writing palettes to: func get_palette_write_path() -> String: return xdg_data_home.plus_file(palettes_data_subdirectory) + # Get the path that we are ok to be writing brushes to: func get_brushes_write_path() -> String: return xdg_data_home.plus_file(brushes_data_subdirectory) +# Get the path that we are ok to be writing patterns to: +func get_patterns_write_path() -> String: + return xdg_data_home.plus_file(patterns_data_subdirectory) + + # Ensure the user xdg directories exist: func ensure_xdg_user_dirs_exist() -> void: + if !OS.has_feature("standalone"): # Don't execute if we're in the editor + return + var base_dir := Directory.new() base_dir.open(raw_xdg_data_home) # Ensure the main config directory exists. @@ -112,6 +129,7 @@ func ensure_xdg_user_dirs_exist() -> void: actual_data_dir.open(xdg_data_home) var palette_writing_dir := get_palette_write_path() var brushes_writing_dir := get_brushes_write_path() + var pattern_writing_dir := get_patterns_write_path() # Create the palette and brush dirs if not actual_data_dir.dir_exists(palette_writing_dir): print("Making directory %s" % [palette_writing_dir]) @@ -119,3 +137,6 @@ func ensure_xdg_user_dirs_exist() -> void: if not actual_data_dir.dir_exists(brushes_writing_dir): print("Making directory %s" % [brushes_writing_dir]) actual_data_dir.make_dir(brushes_writing_dir) + if not actual_data_dir.dir_exists(pattern_writing_dir): + print("Making directory %s" % [pattern_writing_dir]) + actual_data_dir.make_dir(pattern_writing_dir) diff --git a/pixelorama/Patterns/.gdignore b/pixelorama/Patterns/.gdignore new file mode 100644 index 000000000..e69de29bb diff --git a/pixelorama/Patterns/Brick.png b/pixelorama/Patterns/Brick.png new file mode 100644 index 0000000000000000000000000000000000000000..d16085a1cb95cd4f7c44c2905b4f7c23a3019a9c GIT binary patch literal 912 zcmV;B18@9^P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00Q_)L_t(o!>w1bZrd;reF6wg zhBO5+;K4(_px>0e0kRbNfTB|hbZ+LL`GI^voZWyk88GC*fLsYsY8BAv4%CrH>PX8$ zxk%JelyvXiyQk#-3P%WIo4ND(0nxF|+%_#+Kb2L?4P)VUM$ zF!y6Ioe^~)7qId-1LPQ`-LHMl8y;7tCQ}>anEHB9E`UvAW3-MiCr%hH>__OpSiC|y zAL|u7F0C~jkkZUCGwGYewV#(B*8!<_+&lq_V;&lL%{0Fj^xxwV>52K$Lhc#!1E2_V zJ_y%sW-$kJEfc1jrcbGdDaG5=<%Hie3AN_&g+}3y0UV%83NdE=rVt$G{zCUn0VaN|~4d5OwgDJ3gwVf(v{&-}faAdPd)goJi2tKED0&!S%CY7BJXGVxPeeRm?7u;AZ65eHPrHr( m>yL!#TKJDN|Amr{u<;LcG)Daymc~v100005(ej@)Wnk1 z6ovB4k_-iRPv3y>Mm}+%qSu}-jv*GOOM`dw9Z}%ki zT2|x&6Mk#6`R@z=WSzU=b+B~a3d_fG%%K|f? z4;;fkbjFpupny0>fTXE^Ovl6Sm d&fTk>^@}yceorzF{Q&efgQu&X%Q~loCIE1&m$d)@ literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/Ground_leaves.png b/pixelorama/Patterns/Ground_leaves.png new file mode 100644 index 0000000000000000000000000000000000000000..5a2869758eba20a1c766a4869083d98bdb6c4661 GIT binary patch literal 8450 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⪚>&#MS52*6w7OEkDo(X} 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 AmH+?% literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/Metro_tile.png b/pixelorama/Patterns/Metro_tile.png new file mode 100644 index 0000000000000000000000000000000000000000..b44bf5bc0a9f7846876786cc2bf40016b57ab4c6 GIT binary patch literal 2275 zcmV<92pso`P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00>J-L_t(o!(Eq4uVu$o$A9Zl z`MeB#*Fy{Af%WC1yFknS0 zwLK#6w}1aC55D$IOaknZ@BYnS@U53####e}*MIm1_wPPW;$#|+-g=EMJ@aKm4Yh@T z`sYVH|Ln7jFt&tufATg@|MoMSPLJvRgaeGzFJ}Sg=eQMH1*Y>vH9jD8N2)@>Nj`<= zV3C#gudi8aPmWGR2Vm=gXkjrI%_u|yC+7yWA|wL1suJT!Wq>HE$~e78_8r*#VBH34 zis_L8gw7T_Qi?tX*-!K_sD8_cp!X9ZM*0p_WJjc+#je)3c*E$tT{8GaQ8X0 zJP~>zVt^<@QgXoEFkzl0Hz*K+k_G~&btAJ7GmP1RCEp`sF#2}J1e9H3oNzaT8nZRx zhzjltVtds0^ghsbhf>F+SlU9$4v7=v+~SH2jm|cb+vAZaqlp4SRdLEIGHw`Y6Vz%f z%fjF(ye*B&1exe`HOls=EiAfHRk(DdHmo+>jLaf)kTDBCb)g~|F@U17NE#b^GK(<= zRSV)1{kB}PFi19hai2Xz0L9&D=BPKy4wySCAS5XZZpN9Z*(ADKKd(bVQP> zpsJ9#LHrK1Wfs6dRSCI7{_)RVsd@T|j;(jLt#jHodha}Ob;YtYs=!PU5i*mEDTrV4qeriD?;C$amC*8ie(=WM z@!Z{4aM>sk-hT9u`!C!F3X$Y5zw;_Def?!dKhpb&*WY}L7r**E&7DH|*Ps5Br@#0D zYI{sJ-g)~qo_qEMQV$Hdz$s|%s5&Kp2qOhb$#gz={Ft#_Gt?<*mvlfO%^av zNlYimOchbZbU{QgGhB{@?AZFC>l36n8aKH4Y-?o^IXIqHayw33WS_HIuL;>96q!Yw z{s-}e^c{#0VK|-3GBEgbKs^5dLS#Q+&CVOPy@wKfj$QK z0gde}9zYEuipV+hF%kq+a&APS=BV$9OoX5iguuWlCnj6aagb__s$||(_ELH?xx2fM{3u;A1BFm>R zNs0!*&^w5}4YRII();W{W}&iT(y1y23tp$GFR%W| zi_s z&J9FxcjpUV_zK>3h+pvL8-LAncVDI|aTOju{0`sz#<$2AgfxEk?jwHp*#}TXUvK!y zj~?;muiQg@VR7Z*!|!qb-X8!7am$bYRU5S25KL@h)PL~`0~9Z6}FU7YEqNZpdz$V?=ziLrz1K(SwpyIGeS^nnD!|| z<7~4Mhzb!Ks&nIIY24-|GLH1B>8?F+Hc;WXo``;=*R({3V!t!gobI3I_IPCRX}x41 z2rhxjAObRrqCk`^VT|B-n&S{%F)=v)7m-2dN!G2io*uJNs5W{Z^!0`ykqYFyfBK+~ z>pFG5KEeL~Cj_lE-2Lq96Ujv(fZ6nW5yfo=+sL_I@11q)WX_cB&2cj#5;etJYkckQ zb8LrSL%i|&_x_yUf9cz_@!yPXacRu%9 zR0#$A`!D{JU;oUnA`~Ug&wl=IeDUeee-u>y;}<{YH-7VT?7YzB3B0+n?mmMnP{HNJ zfhdq6oU<=gMPbu=GtbgH3s~C6%1D6`yEY{(mg&a400qD z8xg@a3fD_a8}5cXY&mED>8G9~BPS5+Jb$2M{*KYlcfT1@MP@mspu0Pq){Uzr5Lu|& z@TE0+I}#u&8bALH}v@rZCvbMz6I*_3CB)*M^zOd7Y# zISGFft80QiZl?HnpB^2yUt;b=|9~~;;^xLkrAlUN&o?0vJ)d~cTAK@tu&yiSPT3`{ zV`jqL6SxE-({YuR!5*y@6%jTrs2n7YsUWd20&?8oW}Ldx$4XY61!!K~Wma;g;0zP_ x;q_gFX7d8t+QNlCM*P5T*XUf)xgcfX{{YUZ(01xVE4=^!002ovPDHLkV1jf~Lc#z5 literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/Purple_Texture.png b/pixelorama/Patterns/Purple_Texture.png new file mode 100644 index 0000000000000000000000000000000000000000..5a2869758eba20a1c766a4869083d98bdb6c4661 GIT binary patch literal 8450 zcmeHLc|4Ts+n-8VN>Y&}nk-qzEY=a(m%$)AS(*)lF=J+|St1mbUDhZmOQ)12!bHiI zkV7VeN|v%lDy8IoM*Ys|yzhC>?>(Q-`}^-;%-q-gy}sA=e4p#OpZlH!YfBSe9uXc8 z2*isrHM9ZV!R!~;HsC)kEbKi9wEbn6ofF*#6AY$M$(}?%0+=33A%F=XL{AVXgmojE z_^VVIZ~Quq=Rh=Cu^rRJ^`g0bMG}1d-h!~zxt4F`4Hp!5$38bIdc4ZiIk3$4O~1O; zuv7YJhGNw1wi!`!qIMKX=dkF?^0(y`g(K_K(eQhVt~`zL%eRY9LJh`0JLk+JFYcg~F4IV&H!XFb-}>?fvp*Yv+_FD?vT&~aUlt{&o!XyR%*m~o^c zDr~fKrT#PbblZIUBe@8NZY@>*%F`J~VS=d@xeXe;4+BmzO)^z>zvkofGKF>g}D0jQ4-<{bV%`aLbItA38L`OfE<-gUnY^j@g zMwhE?>#kd!TLU_W<`|tlsU4ej&p(Mcb8pMpJxl4}h#jViC+wP8Bk7~!Gp&VPLI-tC zO}Ft2W*$1;D!VAtzWH=6M}kC}lzFN=B(9jpTuvr%;DSMtGxZTN%ax`qo#9kmoMtXp zQ$6F`9B{R$-Nom|joaS1skYJcn{EWYFi3U_o4K3ienRy5IoPX|RAX_%K;N}OOwo9b zX8~bw=)y5;Vym>mjmAgi*R`$Zxvjz{P*#zTY`go9l)5>sFMfhr)Ap%0rT!ZI`ILww zEwY5-BU6+eot?b1xk}2aP}cIhI=&!}iHEW8 z*Y9_OV%MXbvpk1={mxQnXHGq|GGX%Y z+VjSi%4aT#8e!4R*YqtT!+b24FKYI2-EdGhe&sKmjDH#jP50}*{Y)wI!br6im%HDw zp%w9Ao`gAbcgQaTqrZd&=N?qGX{4(P`>qykIk7@w`RGqSuF))8VmyB0#&@*E)A?H7 zuhJ7EzG6aShtp8G6{p^R5@a41fY&<;o>{)HH=r`?WKp*4neALxGdCaL(Xp3>59GL< zyvM?XVF2#4z=T&kTIg zLe6VFX%cuZp3C59pFsc0G|DGu9~bj|SkNWP)6Wi=SRvk7M$O_Y=?|NiXN*1_7vrYv zm8#t9fHn>fAM3xL&y5bYcjBx9ojH7n3lj&LZ(K<5F)=<^?O72w4))oo2b6{9HG|D=+CP zat57TdKqc@Rn{5n<=1=s_aKU@uRdWM#mw1@9@>V+M1Gw+mz63ps5yQ?&fMxN+;)zo zF&CH^=`mP`<~4#RoB~;Tj$a8f7ihA9E2n9QYI(f+T>murLJ?L$r*;g~u`eoTM zRQ93kVv<9NgvlQGSwt9d9%U>VR8&>9Y!K2T6muFO7e^_O5gUyb^fl>%t32)Be&s3WV58OoBinV2d@x0$9c1bf@O1^>wRvf z$a_b~?>=m>tIM#vxo6_OjJD8#ENo?QZ%%ofJpcZWm4;idZ=E@AMIPvXaJYkCWECub zASD^MP`yiz!C}Z)pzjKV8E;zhjT;|gx*fVWX%^||Z}eG7cW>awYwlUKUa4;2AjUuv zNA_&?rlPCJ5RhH5?15mx%kfyV6HU_%yv)!WrEAYp1dRtx6)Gibt`Xd$E;CN5agWvJ zJ(pFB<@3uq;y4@Ne&$n%D)K7%{t`;;v3LWA@__|#1NdI-1VJgUlFuZ+UV?>swwW+x zd#(OL8P-mwg0feFcQv-+8pF;q*kr{h24ZURpaDG;s~T-X@3;aBkKSq@a_`krQ&4!S zk9CoLkd`!OIe5!JdAD0h`d$9vgKL5D_s-wH6SBJ~W_y+@lxQipx2f{}x%%e2WOtW7 z<$Xt88xAU~DkvZCQ0VI3Bx-WhafGf4Nr*>JW*O+Z)wPwBH)SSA`3x0z9G#rLBGm9y z_wDW)otn}fSHiRrsrijgxf(t;vyK z7`_7)933TbTr`AOHPjmJ38&qp!7J3$8hk-b3ZKe!%bk*ro)B*8C#e`*=zb#NERHiB= z<^wT(N8>q5Y3iLFhS;oy&Gt>yr*FLlZiw4i>RjVa;ll{HS9OBj1uVL(2bsJtPdwGc z4pCE?lZLpX(&4BWxOF9Q>oJYVuk;V*1RnEV_rdvNMa(u+%NoXcX}5NzNA8f8k(W6Q zkqCM>`U_{BR{U~C<>z_)@-!YpX#H;B`S=r8b!&_0mz@`b0& z=^=D1Ny#eM;?aBP084>mV13v>vy@fW*vXLB+S@xQ6Ifzl0n}p+kV7@1F%;1G-j7p7qi(Su5t*CNS&0AAQ z3QMk|1AFN{GQrwO-;0BPJt4ok+nPIm^lgArZ*6a;`reoez-W1CKBw_Rd)70vM1_XMt`bZrbY13)!MbeZ#Nsg2s?Z9Io-vv z*|1f`z@7WtM-m-cetqHd!of&Fu?oX#I6@42bm?qJ|3ZU~ms7>?D)9r$CTG-kzJ;*% z-d^!+AgZ$=BBK05!)GSV*aloBY9IOj(0ksejW;@(d+ZbMz*qL?->;4z6*)gY0ryMR z+1@bnz(0*4qX`@4{LtSqw@ApM%qO{6$Uh&IM>nDMlC6x8p)3!Bwc_4i58&o{abD-I zw)S@Ft5%Tg%_im+Uk`o~={#%38i|xeTin)qqbpaXPK{FTTNPd51B-kP0>4c3?a6)~ zw2Mb1xXnYyCt$A7WkFFV()g}EYbI!aUM6)~Q2rk25cIVg|2-Lj92U-?^=8BzGB4@V z>eDham5YHpLSupuGh3$W<=fLJ1NVg=Jg4a$jxZcxRXkw@Nex`7dIy*PHsoLtn#Vs` zQ}cAx1AJ#GU~eIzp#R>-%J#_WRAcLQ)wigjykS`W$$efe0D_OWZ{Ii(8Q5RN*FvDOHc|SQGhf+AT6B` z3I^v(po6glFQUJ;#M_1@2`~|_E#at&hM_421aG2g7?offW@(2D^Ti?Y5;{kCv_doi z0uq6a0f&(M{ArpY+7cVQn!sna87cwZfY5!lC7jUKU;{Fh09H{_QG!8?LWn^Ki6cB< zEh^qq)5g&F2L&+Fmhh(2DVk7daB#3vu(A@F>IH=(kw_>E0YxAn00Ke_^`~P(ApSH- zHpO=iLjny)1+*iQ{lRQb43->7*Org~=D|P6*~%J)0LC8y*y9a)8XXTs0Rt7_dH?_v z0fT8kU-9_jrHmL9+u6cu*Sx zjT}hD5sZQe{&dNogHrqgX+Osdq!HMrjlBKvo=_mEjmSUyn4r+szxl9b^dgcd8y;-* z&qzG(HykC9>bHTxx3uLpP47DZL325TU zI3iwix@uvIY|7iNL7hRXx>yqC)x8=oo(-flURF zD-i)6HB|%*3sb{GFv=P@hzf$B3_+^ER3M&kg0d=3T?4L6_?g0rN(4$9_G5ET^_PY6;?!xI8z0Yl)_FgSp4BOA7`ntIkKZ3%=D?01W`ABOHp zrjmeuA^PLV!L;9}?1&_SEgi#_4X&!D0#}8pz+ph1s;cV0gB%D{8c?@vRya%vu`z+i zX&M8R7@&NJB#ajUO7ZvFm;jR2G@ueNbTZYBO!m{3U?&A;du}uXSnEfbYg&+T76!mW|>c8Q%zSpHG;7g{4{uJMq5b%BUeJSxHZb$_NZ?vx_2FF%WTY`oO zBH%Y}0ifgeDV#UP--`gOlRui|Uv}c3)T;^trlx{Zg+VX~BpxU-3LP9uBLgE3Tso)^$Xz$4NM*geRe};UQ<);S-kn?vN@L&R- zbI{)&bU&(u-AMnzpC8rtAB+G{{}beI>HCjd|H$>X6!=@{e+&Fib^X7| z#q;}7ncxrX`+|Xk@U}~v27yELHmtdcA!wcbdx=q!4p_J;rp`1FNJ*0Y+7xnCCkQZd z(otw5&T&qWoxI{1O>G8%NtkZrL^mLl*oP(1kKH;rm`L{qv$yt+(L48nKpeMFhI)2R zErE`={l|@WMYtSp>#J|cNjY_DI!1z~D>AMHM8>T-vK!q7h!ka+wTo)n zF*ydhG}AbFxlxQS)zTD^vmE&G@hPO`&0n%=Uk?g{j;BNWxH+GZm}k<vH%TVTI2`3f`(NS`E0+g^_n<+*-$LSeH6}+66Q+@b(ts|+-yrPKJo;0^Y+J_ zo?Y=NSrhe^8X9kOQ%eB3UDQm*`4hFCVzF(k2@rGF(dk%JnDmgGx_KWr3nI0gX>9!b zF`$}k-%EF4v-D!40_<*HKCX`Q84ms2%(o6ZQ{S0IBjQY{Z?a{2u53XhX=Q{1v}OUR z>S=G{829c-_6P6gRruIFpryS(D8*4EMg7=g>`S71Vv->wB)23z@M)T?8i{Yy$u_di z`nSdWf!rs#ly{!jGP+z2lHDAyWe#zgPN|cu_E>8^td{zWFOO#&5v&sG3$YtZReYJbEo<`Llg$1j2=>CF( z7`&^yS^ind6Ra`5>gHy2DrlB}Q>VoP<2qEwuVC=9lf2YC$Gn3Pr8HZy^CIO^Xdsoq zXbfCa0`5VNqG<4naJ~Yk*M;*;=#jB^ulApHK1Y5LRdMzSR&e}5^We7 zzW4Bu$17G-*`;RvpwNJ%(V+9dU#nspV6~f499HDv9?Mc!%sjO?y&@&@U~_@~a;dOQ zm~^t#7wI(lxev)<+TuNE#9=Ns74gea3A@+q62RW_gXm2o}LQ_cDjL z^1{mwdQ}{sS8n_Oe8a5K4g{c$3oI-sbIc3A!k4#H4&O|D>`1w}e`M5)m7|eU^a-Y| za<$UG%iWzP!4>DATBVO5UA<9XV3YZ%Z)===k7D~o^o}iY#v;lJm#6ccGhCaFP3npT z%xI~Po0+72HjDDwIuCT~meWbg=WOMq>|hBTgpW_wuSm2!j>YMn^)jA^Lc9DQVPTJ}wFbcS`+ zFM}VbdJSyBDxdm*C!O5KBjpxrMeDxRaYe zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00M(aL_t(o!=+b2j)O1^oJL3} zmxx#LgmUN~x%D3*7mG`>hsx9#C#2d)tECRMV`n@zD5aEb+lsZeZ9DDh=XsvXf7U$D zWk@OE{eA-g#&N{H@6~51lWSeqW8Yf5M??Ss=N!gy#57G9LI@WptfUQC`zfWW9xvAQ z-h+rvfehM-HX>y;Co=L@07L}m-0>d(TuKS!I9^gNm1|CVm&^dKobxvWmXkY2e23Ou zw`^(o5JE5={Ssv@bJ~W!3IK3&lmE0#I+6P^#>24{vDT55kaI?i@pAwTIvtTT)G9Tr z0yKCf4o(^9MZ1(S23W_+LJi!P14tt+)Ttn5OA_0GY;LszDN2&tU-^ zIEXP~U>>-uVSOzOqLh+(bu}tyDkh>273;JyL2#-pMw3xk;M5tg^iE|cPwKsga}Lfq zGb%R11~dkU2t-t^tVdeOEhg+e$lVy%I^fV##}+B=`+n+%Ejw2A5Ye|AwwO@2 zG^G5pyWz?>39awY)ZvNPu$6`9Lg}K|G$%XNfUh+HtLY%GTQ3j%&XG Z;1B9G36RrBtHS^Q002ovPDHLkV1hMZSmXcz literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/Wooden_planks_1.png b/pixelorama/Patterns/Wooden_planks_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a15a664ca002bddf26a14107720922ec6c46f23e GIT binary patch literal 6223 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 literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/Yellow_Marble.png b/pixelorama/Patterns/Yellow_Marble.png new file mode 100644 index 0000000000000000000000000000000000000000..b44bf5bc0a9f7846876786cc2bf40016b57ab4c6 GIT binary patch literal 2275 zcmV<92pso`P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00>J-L_t(o!(Eq4uVu$o$A9Zl z`MeB#*Fy{Af%WC1yFknS0 zwLK#6w}1aC55D$IOaknZ@BYnS@U53####e}*MIm1_wPPW;$#|+-g=EMJ@aKm4Yh@T z`sYVH|Ln7jFt&tufATg@|MoMSPLJvRgaeGzFJ}Sg=eQMH1*Y>vH9jD8N2)@>Nj`<= zV3C#gudi8aPmWGR2Vm=gXkjrI%_u|yC+7yWA|wL1suJT!Wq>HE$~e78_8r*#VBH34 zis_L8gw7T_Qi?tX*-!K_sD8_cp!X9ZM*0p_WJjc+#je)3c*E$tT{8GaQ8X0 zJP~>zVt^<@QgXoEFkzl0Hz*K+k_G~&btAJ7GmP1RCEp`sF#2}J1e9H3oNzaT8nZRx zhzjltVtds0^ghsbhf>F+SlU9$4v7=v+~SH2jm|cb+vAZaqlp4SRdLEIGHw`Y6Vz%f z%fjF(ye*B&1exe`HOls=EiAfHRk(DdHmo+>jLaf)kTDBCb)g~|F@U17NE#b^GK(<= zRSV)1{kB}PFi19hai2Xz0L9&D=BPKy4wySCAS5XZZpN9Z*(ADKKd(bVQP> zpsJ9#LHrK1Wfs6dRSCI7{_)RVsd@T|j;(jLt#jHodha}Ob;YtYs=!PU5i*mEDTrV4qeriD?;C$amC*8ie(=WM z@!Z{4aM>sk-hT9u`!C!F3X$Y5zw;_Def?!dKhpb&*WY}L7r**E&7DH|*Ps5Br@#0D zYI{sJ-g)~qo_qEMQV$Hdz$s|%s5&Kp2qOhb$#gz={Ft#_Gt?<*mvlfO%^av zNlYimOchbZbU{QgGhB{@?AZFC>l36n8aKH4Y-?o^IXIqHayw33WS_HIuL;>96q!Yw z{s-}e^c{#0VK|-3GBEgbKs^5dLS#Q+&CVOPy@wKfj$QK z0gde}9zYEuipV+hF%kq+a&APS=BV$9OoX5iguuWlCnj6aagb__s$||(_ELH?xx2fM{3u;A1BFm>R zNs0!*&^w5}4YRII();W{W}&iT(y1y23tp$GFR%W| zi_s z&J9FxcjpUV_zK>3h+pvL8-LAncVDI|aTOju{0`sz#<$2AgfxEk?jwHp*#}TXUvK!y zj~?;muiQg@VR7Z*!|!qb-X8!7am$bYRU5S25KL@h)PL~`0~9Z6}FU7YEqNZpdz$V?=ziLrz1K(SwpyIGeS^nnD!|| z<7~4Mhzb!Ks&nIIY24-|GLH1B>8?F+Hc;WXo``;=*R({3V!t!gobI3I_IPCRX}x41 z2rhxjAObRrqCk`^VT|B-n&S{%F)=v)7m-2dN!G2io*uJNs5W{Z^!0`ykqYFyfBK+~ z>pFG5KEeL~Cj_lE-2Lq96Ujv(fZ6nW5yfo=+sL_I@11q)WX_cB&2cj#5;etJYkckQ zb8LrSL%i|&_x_yUf9cz_@!yPXacRu%9 zR0#$A`!D{JU;oUnA`~Ug&wl=IeDUeee-u>y;}<{YH-7VT?7YzB3B0+n?mmMnP{HNJ zfhdq6oU<=gMPbu=GtbgH3s~C6%1D6`yEY{(mg&a400qD z8xg@a3fD_a8}5cXY&mED>8G9~BPS5+Jb$2M{*KYlcfT1@MP@mspu0Pq){Uzr5Lu|& z@TE0+I}#u&8bALH}v@rZCvbMz6I*_3CB)*M^zOd7Y# zISGFft80QiZl?HnpB^2yUt;b=|9~~;;^xLkrAlUN&o?0vJ)d~cTAK@tu&yiSPT3`{ zV`jqL6SxE-({YuR!5*y@6%jTrs2n7YsUWd20&?8oW}Ldx$4XY61!!K~Wma;g;0zP_ x;q_gFX7d8t+QNlCM*P5T*XUf)xgcfX{{YUZ(01xVE4=^!002ovPDHLkV1jf~Lc#z5 literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/small_stonebricks.png b/pixelorama/Patterns/small_stonebricks.png new file mode 100644 index 0000000000000000000000000000000000000000..38dc8326dd3bc7d3a3dbba64098bee2fedb266d2 GIT binary patch literal 1019 zcmVe zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00U!5L_t(o!<|<>PvbxkeLijB z#)ybmibRtikmCLT9o2O-R~|ZwyAB~8J));L@dH3Kw1|p^1|gv2D@8hz0u+fvHrYDj z3hOPi-e<=0+^kF1>dOGY=Ef?P z77~mn7KbNy*j``3cw(`%kYMNU2{t!YUBUjrxjPS_Hb0(N>>r$Cdwm4}y#DS7*lYw7 zrMP+=x_|d(WAp|&fE-Dq2@|F0_J^*hH<$v*-M=ehl=7W+3rVAiHyal>i(=SpgsaCQ z*X<8sl13BV{tyrMx2~a#0psH3bkW^}d7I@fX1>#I;rR3nFdh3}ycCGdeFhv%9s1@x%fEz7Rtbr4Gx26FGW= zDP-P6DQq@EZ!pE1jbWnHVa^)X%RF=B0&~5=w0HslaC~~^Fzoh+7{5H>`85Y%9H#P; z=R+|umK@NWSWXuag>;|_zgg!rkLKxXY&HsVKw?VFbga)yj)dw%=M6EEbN&$*kC52P zm~;Fw_r>`6aqM)!L@~PkVM#X}2R<-|(gApL&c{qnFn+kd#lwA}3*-!A*^j!x`#Bw$ z2XE~Mvv^VE0kYQ90b)iBL%JZnqRJaIXLNlH2XYT1YPs}^Dh_nov5O^VLiMu0snhn* z+{(Gvx}Z6uy@YY6-NME74K7}u3Jypt>%4m&pQz)K+%x#SNg7QLQ$5FfN0%NaGza8) zaw5cmke4_i;+%$bfZ9*Y{e8j5RC0tmAp3RNE!U5lQrZXf+-j{4o#)(3ouDpA4l$PA z64ZgudV<~|>wG}h8vrG(-rZVrJDk?;d3L^#jz~_)`x$?3{W&Mj8e=`z^m(2S?U0>z z%hgD42zr5@>(D+B;)tAk*^hJ2{epRq1M~X*JlIyvf5;;o2dXfir3=~%_Kq$~ULki# z-Ou=Q+ww3Yo4MOvJr2E?{!UJ?MsrHey{`*#@dKoPhx}f8fzC_apm%7___6VH!qb7z z_`v_{jiG&_v=2y(8Efqi?TDH~q4$E&dx9Z`A&fb<8dG`!wVt{`Khc=WcSX5BI5X0I pAKKxVm*002ovPDHLkV1nYY+?@ab literal 0 HcmV?d00001 diff --git a/pixelorama/Patterns/wooden_planks_2.png b/pixelorama/Patterns/wooden_planks_2.png new file mode 100644 index 0000000000000000000000000000000000000000..58f80dcf4557d7e2a38bdaf1be9c66385915edda GIT binary patch literal 7417 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