diff --git a/Main.tscn b/Main.tscn index 84581119b..742a0c63f 100644 --- a/Main.tscn +++ b/Main.tscn @@ -637,6 +637,23 @@ value = 1.0 allow_greater = true ticks_on_borders = true +[node name="LeftBrushPixelPerfectMode" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions"] +margin_top = 71.0 +margin_right = 160.0 +margin_bottom = 87.0 +alignment = 1 + +[node name="LeftPixelPerfectMode" type="CheckBox" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftBrushPixelPerfectMode"] +margin_left = 36.0 +margin_right = 123.0 +margin_bottom = 16.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +pressed = true +text = "Pixel Perfect" +align = 1 + [node name="LeftColorInterpolation" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions"] visible = false margin_top = 75.0 @@ -1031,6 +1048,23 @@ value = 1.0 allow_greater = true ticks_on_borders = true +[node name="RightBrushPixelPerfectMode" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions"] +margin_top = 71.0 +margin_right = 160.0 +margin_bottom = 87.0 +alignment = 1 + +[node name="RightPixelPerfectMode" type="CheckBox" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightBrushPixelPerfectMode"] +margin_left = 36.0 +margin_right = 123.0 +margin_bottom = 16.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +pressed = true +text = "Pixel Perfect" +align = 1 + [node name="RightColorInterpolation" type="VBoxContainer" parent="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions"] visible = false margin_top = 75.0 @@ -1658,6 +1692,7 @@ visible = false [connection signal="pressed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftBrushType/LeftBrushTypeButton" to="." method="_on_LeftBrushTypeButton_pressed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftBrushType/LeftBrushSizeEdit" to="." method="_on_LeftBrushSizeEdit_value_changed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftBrushSizeSlider" to="." method="_on_LeftBrushSizeEdit_value_changed"] +[connection signal="toggled" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/LeftToolOptions/LeftBrushPixelPerfectMode/LeftPixelPerfectMode" to="." method="_on_LeftPixelPerfectMode_toggled"] [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"] @@ -1677,6 +1712,7 @@ visible = false [connection signal="pressed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightBrushType/RightBrushTypeButton" to="." method="_on_RightBrushTypeButton_pressed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightBrushType/RightBrushSizeEdit" to="." method="_on_RightBrushSizeEdit_value_changed"] [connection signal="value_changed" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightBrushSizeSlider" to="." method="_on_RightBrushSizeEdit_value_changed"] +[connection signal="toggled" from="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/ColorAndToolOptions/ScrollContainer/ToolOptions/RightToolOptions/RightBrushPixelPerfectMode/RightPixelPerfectMode" to="." method="_on_RightPixelPerfectMode_toggled"] [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"] diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index 515ee404a..d1791d7ba 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -27,6 +27,17 @@ var is_making_selection := "None" var line_2d : Line2D var pen_pressure := 1.0 # For tablet pressure sensitivity +const Drawer = preload("Drawers.gd").Drawer +const SimpleDrawer = preload("Drawers.gd").SimpleDrawer +const PixelPerfectDrawer = preload("Drawers.gd").PixelPerfectDrawer + +var pixel_perfect_drawer := PixelPerfectDrawer.new() +var pixel_perfect_drawer_h_mirror := PixelPerfectDrawer.new() +var pixel_perfect_drawer_v_mirror := PixelPerfectDrawer.new() +var pixel_perfect_drawer_hv_mirror := PixelPerfectDrawer.new() +var simple_drawer := SimpleDrawer.new() + + # Called when the node enters the scene tree for the first time. func _ready() -> void: var fill_layers := layers.empty() @@ -219,6 +230,10 @@ func _input(event : InputEvent) -> void: made_line = false mouse_press_pixels.clear() mouse_press_pressure_values.clear() + pixel_perfect_drawer.reset() + pixel_perfect_drawer_h_mirror.reset() + pixel_perfect_drawer_v_mirror.reset() + pixel_perfect_drawer_hv_mirror.reset() can_undo = true current_pixel = get_local_mouse_position() + location @@ -610,8 +625,11 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt var brush_type = Global.Brush_Types.PIXEL var brush_index := -1 var custom_brush_image : Image + var horizontal_mirror := false var vertical_mirror := false + var pixel_perfect := false + var ld := 0 var ld_amount := 0.1 if Global.pressure_sensitivity_mode == Global.Pressure_Sensitivity.ALPHA: @@ -637,6 +655,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt horizontal_mirror = Global.left_horizontal_mirror vertical_mirror = Global.left_vertical_mirror + pixel_perfect = Global.left_pixel_perfect ld = Global.left_ld ld_amount = Global.left_ld_amount @@ -658,6 +677,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt horizontal_mirror = Global.right_horizontal_mirror vertical_mirror = Global.right_vertical_mirror + pixel_perfect = Global.right_pixel_perfect ld = Global.right_ld ld_amount = Global.right_ld_amount @@ -667,6 +687,11 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt var end_pos_y if brush_type == Global.Brush_Types.PIXEL || current_action == "LightenDarken": + var drawer = pixel_perfect_drawer if pixel_perfect else simple_drawer + var drawer_v_mirror = pixel_perfect_drawer_v_mirror if pixel_perfect else simple_drawer + var drawer_h_mirror = pixel_perfect_drawer_h_mirror if pixel_perfect else simple_drawer + var drawer_hv_mirror = pixel_perfect_drawer_hv_mirror if pixel_perfect else simple_drawer + start_pos_x = pos.x - (brush_size >> 1) start_pos_y = pos.y - (brush_size >> 1) end_pos_x = start_pos_x + brush_size @@ -696,7 +721,8 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt mouse_press_pressure_values.append(pen_pressure) else: mouse_press_pressure_values[saved_pixel_index] = pen_pressure - sprite.set_pixel(cur_pos_x, cur_pos_y, _c) + drawer.set_pixel(sprite, Vector2(cur_pos_x, cur_pos_y), _c) + sprite_changed_this_frame = true # Handle mirroring @@ -713,7 +739,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt mouse_press_pixels.append(pos_floored) mouse_press_pressure_values.append(pen_pressure) - sprite.set_pixel(mirror_x, cur_pos_y, _c) + drawer_h_mirror.set_pixel(sprite, Vector2(mirror_x, cur_pos_y), _c) sprite_changed_this_frame = true if vertical_mirror: @@ -726,7 +752,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt _c = current_pixel_color.darkened(ld_amount) mouse_press_pixels.append(pos_floored) mouse_press_pressure_values.append(pen_pressure) - sprite.set_pixel(cur_pos_x, mirror_y, _c) + drawer_v_mirror.set_pixel(sprite, Vector2(cur_pos_x, mirror_y), _c) sprite_changed_this_frame = true if horizontal_mirror && vertical_mirror: @@ -740,7 +766,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt mouse_press_pixels.append(pos_floored) mouse_press_pressure_values.append(pen_pressure) - sprite.set_pixel(mirror_x, mirror_y, _c) + drawer_hv_mirror.set_pixel(sprite, Vector2(mirror_x, mirror_y), _c) sprite_changed_this_frame = true elif brush_type == Global.Brush_Types.CIRCLE || brush_type == Global.Brush_Types.FILLED_CIRCLE: diff --git a/Scripts/Drawers.gd b/Scripts/Drawers.gd new file mode 100644 index 000000000..b26251f91 --- /dev/null +++ b/Scripts/Drawers.gd @@ -0,0 +1,37 @@ +class Drawer: + func reset() -> void: + pass + + func set_pixel(sprite: Image, pos: Vector2, new_color: Color) -> void: + pass + + +class SimpleDrawer extends Drawer: + func reset() -> void: + pass + + func set_pixel(sprite: Image, pos: Vector2, new_color: Color) -> void: + sprite.set_pixel(pos.x, pos.y, new_color) + + +class PixelPerfectDrawer extends Drawer: + const neighbours = [Vector2(0, 1), Vector2(1, 0), Vector2(-1, 0), Vector2(0, -1)] + const corners = [Vector2(1, 1), Vector2(-1, -1), Vector2(-1, 1), Vector2(1, -1)] + var last_pixels = [null, null] + + func reset(): + last_pixels = [null, null] + + func set_pixel(sprite: Image, pos: Vector2, new_color: Color) -> void: + last_pixels.push_back([pos, sprite.get_pixel(pos.x, pos.y)]) + sprite.set_pixel(pos.x, pos.y, new_color) + + var corner = last_pixels.pop_front() + var neighbour = last_pixels[0] + + if corner == null or neighbour == null: + return + + if pos - corner[0] in corners and pos - neighbour[0] in neighbours: + sprite.set_pixel(neighbour[0].x, neighbour[0].y, neighbour[1]) + last_pixels[0] = corner diff --git a/Scripts/Global.gd b/Scripts/Global.gd index 7fc332e43..6abe7fa75 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -105,6 +105,9 @@ var left_vertical_mirror := false var right_horizontal_mirror := false var right_vertical_mirror := false +var left_pixel_perfect := true +var right_pixel_perfect := true + # View menu options var tile_mode := false var draw_grid := false @@ -195,6 +198,9 @@ var left_brush_size_slider : HSlider var right_brush_size_edit : SpinBox var right_brush_size_slider : HSlider +var left_pixel_perfect_container : VBoxContainer +var right_pixel_perfect_container : VBoxContainer + var left_color_interpolation_container : Container var right_color_interpolation_container : Container var left_interpolate_spinbox : SpinBox @@ -324,6 +330,9 @@ func _ready() -> void: right_brush_size_edit = find_node_by_name(root, "RightBrushSizeEdit") right_brush_size_slider = find_node_by_name(root, "RightBrushSizeSlider") + left_pixel_perfect_container = find_node_by_name(root, "LeftBrushPixelPerfectMode") + right_pixel_perfect_container = find_node_by_name(root, "RightBrushPixelPerfectMode") + left_color_interpolation_container = find_node_by_name(root, "LeftColorInterpolation") right_color_interpolation_container = find_node_by_name(root, "RightColorInterpolation") left_interpolate_spinbox = find_node_by_name(root, "LeftInterpolateFactor") diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 8473b3e3c..05b1364bf 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -495,12 +495,14 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le if current_action == "Pencil": Global.left_brush_type_container.visible = true Global.left_brush_size_slider.visible = true + Global.left_pixel_perfect_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 == "Eraser": Global.left_brush_type_container.visible = true Global.left_brush_size_slider.visible = true + Global.left_pixel_perfect_container.visible = true Global.left_mirror_container.visible = true elif current_action == "Bucket": Global.left_fill_area_container.visible = true @@ -508,6 +510,7 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le elif current_action == "LightenDarken": Global.left_brush_type_container.visible = true Global.left_brush_size_slider.visible = true + Global.left_pixel_perfect_container.visible = true Global.left_ld_container.visible = true Global.left_mirror_container.visible = true elif current_action == "ColorPicker": @@ -527,12 +530,14 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le if current_action == "Pencil": Global.right_brush_type_container.visible = true Global.right_brush_size_slider.visible = true + Global.right_pixel_perfect_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 == "Eraser": Global.right_brush_type_container.visible = true Global.right_brush_size_slider.visible = true + Global.right_pixel_perfect_container.visible = true Global.right_mirror_container.visible = true elif current_action == "Bucket": Global.right_fill_area_container.visible = true @@ -540,6 +545,7 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le elif current_action == "LightenDarken": Global.right_brush_type_container.visible = true Global.right_brush_size_slider.visible = true + Global.right_pixel_perfect_container.visible = true Global.right_ld_container.visible = true Global.right_mirror_container.visible = true elif current_action == "ColorPicker": @@ -784,3 +790,11 @@ func _on_QuitDialog_confirmed() -> void: modulate = Color(0.5, 0.5, 0.5) get_tree().quit() + + +func _on_LeftPixelPerfectMode_toggled(button_pressed) -> void: + Global.left_pixel_perfect = button_pressed + + +func _on_RightPixelPerfectMode_toggled(button_pressed) -> void: + Global.right_pixel_perfect = button_pressed