diff --git a/src/Tools/Bucket.gd b/src/Tools/Bucket.gd index a3a6c04dd..cd663beff 100644 --- a/src/Tools/Bucket.gd +++ b/src/Tools/Bucket.gd @@ -1,12 +1,15 @@ extends BaseTool +enum FillArea { AREA, COLORS, SELECTION } +enum FillWith { COLOR, PATTERN } + const COLOR_REPLACE_SHADER := preload("res://src/Shaders/ColorReplace.shader") var _prev_mode := 0 var _pattern: Patterns.Pattern var _similarity := 100 -var _fill_area := true -var _fill_with := 0 +var _fill_area: int = FillArea.AREA +var _fill_with: int = FillWith.COLOR var _offset_x := 0 var _offset_y := 0 # working array used as buffer for segments while flooding @@ -14,26 +17,36 @@ var _allegro_flood_segments: Array # results array per image while flooding var _allegro_image_segments: Array -onready var fill_check_box: CheckBox = $FillCheckBox - func _ready() -> void: update_pattern() func _input(event: InputEvent) -> void: - if event.is_action_pressed("change_tool_mode") or event.is_action_released("change_tool_mode"): - fill_check_box.pressed = not fill_check_box.pressed - _fill_area = fill_check_box.pressed - $Similarity.visible = not _fill_area + if event.is_action_pressed("change_tool_mode"): + _prev_mode = _fill_area + if event.is_action("change_tool_mode"): + if _fill_area == FillArea.SELECTION: + _fill_area = FillArea.AREA + else: + _fill_area = _prev_mode ^ 1 + _select_fill_area_optionbutton() + if event.is_action_released("change_tool_mode"): + _fill_area = _prev_mode + _select_fill_area_optionbutton() -func _on_FillCheckBox_toggled(button_pressed: bool) -> void: - _fill_area = button_pressed +func _on_FillAreaOptions_item_selected(index: int) -> void: + _fill_area = index update_config() save_config() +func _select_fill_area_optionbutton() -> void: + $FillAreaOptions.selected = _fill_area + $Similarity.visible = (_fill_area == FillArea.COLORS) + + func _on_FillWithOptions_item_selected(index: int) -> void: _fill_with = index $Similarity/Value.value = _similarity @@ -105,12 +118,11 @@ func set_config(config: Dictionary) -> void: func update_config() -> void: - $FillCheckBox.pressed = _fill_area + _select_fill_area_optionbutton() $FillWithOptions.selected = _fill_with - $Similarity.visible = not _fill_area $Similarity/Value.value = _similarity $Similarity/Slider.value = _similarity - $FillPattern.visible = _fill_with == 1 + $FillPattern.visible = _fill_with == FillWith.PATTERN $FillPattern/XOffset/OffsetX.value = _offset_x $FillPattern/YOffset/OffsetY.value = _offset_y @@ -147,11 +159,14 @@ func draw_start(position: Vector2) -> void: and not Global.current_project.can_pixel_get_drawn(position) ): return - var undo_data = _get_undo_data() - if _fill_area: - fill_in_area(position) - else: - fill_in_color(position) + var undo_data := _get_undo_data() + match _fill_area: + FillArea.AREA: + fill_in_area(position) + FillArea.COLORS: + fill_in_color(position) + FillArea.SELECTION: + fill_in_selection() commit_undo("Draw", undo_data) @@ -169,7 +184,7 @@ func fill_in_color(position: Vector2) -> void: var images := _get_selected_draw_images() for image in images: var pattern_image: Image - if _fill_with == 0 or _pattern == null: + if _fill_with == FillWith.COLOR or _pattern == null: if tool_slot.color.is_equal_approx(color): return else: @@ -205,7 +220,7 @@ func fill_in_color(position: Vector2) -> void: # pixel offset converted to pattern uv offset "pattern_uv_offset": Vector2.ONE / pattern_tex.get_size() * Vector2(_offset_x, _offset_y), - "has_pattern": true if _fill_with == 1 else false + "has_pattern": true if _fill_with == FillWith.PATTERN else false } var gen := ShaderImageEffect.new() gen.generate_image(image, COLOR_REPLACE_SHADER, params, project.size) @@ -232,6 +247,25 @@ func fill_in_area(position: Vector2) -> void: _flood_fill(Vector2(position.x, mirror_y)) +func fill_in_selection() -> void: + var project: Project = Global.current_project + var images := _get_selected_draw_images() + if project.has_selection: + var filler := Image.new() + filler.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8) + filler.fill(tool_slot.color) + var rect: Rect2 = Global.canvas.selection.big_bounding_rectangle + var selection_map_copy := SelectionMap.new() + selection_map_copy.copy_from(project.selection_map) + # In case the selection map is bigger than the canvas + selection_map_copy.crop(project.size.x, project.size.y) + for image in images: + image.blit_rect_mask(filler, selection_map_copy, rect, rect.position) + else: + for image in images: + image.fill(tool_slot.color) + + # Add a new segment to the array func _add_new_segment(y: int = 0) -> void: var segment = {} @@ -337,7 +371,7 @@ func _flood_fill(position: Vector2) -> void: var images := _get_selected_draw_images() for image in images: var color: Color = image.get_pixelv(position) - if _fill_with == 0 or _pattern == null: + if _fill_with == FillWith.COLOR or _pattern == null: # end early if we are filling with the same color if tool_slot.color.is_equal_approx(color): return @@ -385,7 +419,7 @@ func _compute_segments_for_image( func _color_segments(image: Image) -> void: - if _fill_with == 0 or _pattern == null: + if _fill_with == FillWith.COLOR or _pattern == null: var color_str = tool_slot.color.to_html() # short circuit for flat colors for c in _allegro_image_segments.size(): diff --git a/src/Tools/Bucket.tscn b/src/Tools/Bucket.tscn index 5e0924d56..828f9a1ec 100644 --- a/src/Tools/Bucket.tscn +++ b/src/Tools/Bucket.tscn @@ -27,21 +27,29 @@ margin_bottom = 105.0 script = ExtResource( 3 ) [node name="Label" parent="." index="0"] -margin_right = 132.0 +margin_right = 131.0 -[node name="FillCheckBox" type="CheckBox" parent="." index="1"] +[node name="FillArea" type="Label" parent="." index="1"] +margin_left = 38.0 margin_top = 18.0 -margin_right = 132.0 -margin_bottom = 42.0 -hint_tooltip = "If enabled, it fills the contiguous area with the selected color. -If disabled, it replaces all pixels of the same color with the selected color." +margin_right = 92.0 +margin_bottom = 32.0 mouse_default_cursor_shape = 2 size_flags_horizontal = 4 -pressed = true -text = "Fill contiguously" -align = 1 +text = "Fill area:" -[node name="Similarity" type="VBoxContainer" parent="." index="2"] +[node name="FillAreaOptions" type="OptionButton" parent="." index="2"] +margin_left = 13.0 +margin_top = 36.0 +margin_right = 117.0 +margin_bottom = 56.0 +mouse_default_cursor_shape = 2 +size_flags_horizontal = 4 +text = "Similar area" +items = [ "Similar area", null, false, 0, null, "Similar colors", null, false, 1, null, "Whole selection", null, false, 2, null ] +selected = 0 + +[node name="Similarity" type="VBoxContainer" parent="." index="3"] visible = false margin_top = 60.0 margin_right = 131.0 @@ -89,26 +97,26 @@ __meta__ = { "_editor_description_": "" } -[node name="FillWith" type="Label" parent="." index="3"] -margin_left = 39.0 -margin_top = 46.0 -margin_right = 93.0 -margin_bottom = 60.0 +[node name="FillWith" type="Label" parent="." index="4"] +margin_left = 38.0 +margin_top = 60.0 +margin_right = 92.0 +margin_bottom = 74.0 size_flags_horizontal = 4 text = "Fill with:" -[node name="FillWithOptions" type="OptionButton" parent="." index="4"] +[node name="FillWithOptions" type="OptionButton" parent="." index="5"] margin_left = 5.0 -margin_top = 64.0 +margin_top = 78.0 margin_right = 126.0 -margin_bottom = 84.0 +margin_bottom = 98.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="FillPattern" type="VBoxContainer" parent="." index="5"] +[node name="FillPattern" type="VBoxContainer" parent="." index="6"] visible = false margin_left = 22.0 margin_top = 102.0 @@ -180,7 +188,7 @@ margin_right = 85.0 margin_bottom = 24.0 mouse_default_cursor_shape = 2 -[connection signal="toggled" from="FillCheckBox" to="." method="_on_FillCheckBox_toggled"] +[connection signal="item_selected" from="FillAreaOptions" to="." method="_on_FillAreaOptions_item_selected"] [connection signal="value_changed" from="Similarity/Value" to="." method="_on_Value_value_changed"] [connection signal="value_changed" from="Similarity/Slider" to="." method="_on_Slider_value_changed"] [connection signal="item_selected" from="FillWithOptions" to="." method="_on_FillWithOptions_item_selected"]