mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-05-05 12:34:50 -04:00
I mostly did it so I could move tile mode logic into a new node, so I could have control over the drawing order. TileMode.gd also always draws 8 blank rectangles, which are being drawn on top of the grid, in order to hide it if it gets out of boundaries. We are now very close to implementing an isometric grid, #305. This shouldn't have much of a performance impact, but some more testing is most likely needed.
242 lines
7.6 KiB
GDScript
242 lines
7.6 KiB
GDScript
class_name Canvas
|
|
extends Node2D
|
|
|
|
|
|
var location := Vector2.ZERO
|
|
var fill_color := Color(0, 0, 0, 0)
|
|
var current_pixel := Vector2.ZERO # pretty much same as mouse_pos, but can be accessed externally
|
|
var can_undo := true
|
|
var cursor_image_has_changed := false
|
|
var sprite_changed_this_frame := false # for optimization purposes
|
|
|
|
onready var grid = $Grid
|
|
onready var tile_mode = $TileMode
|
|
onready var indicators = $Indicators
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
var frame : Frame = new_empty_frame(true)
|
|
Global.current_project.frames.append(frame)
|
|
yield(get_tree().create_timer(0.2), "timeout")
|
|
camera_zoom()
|
|
|
|
|
|
func _draw() -> void:
|
|
Global.second_viewport.get_child(0).get_node("CanvasPreview").update()
|
|
Global.small_preview_viewport.get_child(0).get_node("CanvasPreview").update()
|
|
|
|
var current_cels : Array = Global.current_project.frames[Global.current_project.current_frame].cels
|
|
if Global.onion_skinning:
|
|
onion_skinning()
|
|
|
|
# Draw current frame layers
|
|
for i in range(Global.current_project.layers.size()):
|
|
var modulate_color := Color(1, 1, 1, current_cels[i].opacity)
|
|
if Global.current_project.layers[i].visible: # if it's visible
|
|
draw_texture(current_cels[i].image_texture, location, modulate_color)
|
|
|
|
tile_mode.update()
|
|
|
|
|
|
func _input(event : InputEvent) -> void:
|
|
# Don't process anything below if the input isn't a mouse event, or Shift/Ctrl.
|
|
# This decreases CPU/GPU usage slightly.
|
|
if not event is InputEventMouse:
|
|
if not event is InputEventKey:
|
|
return
|
|
elif not event.scancode in [KEY_SHIFT, KEY_CONTROL]:
|
|
return
|
|
# elif not get_viewport_rect().has_point(event.position):
|
|
# return
|
|
|
|
current_pixel = get_local_mouse_position() + location
|
|
|
|
if Global.has_focus:
|
|
update()
|
|
|
|
sprite_changed_this_frame = false
|
|
|
|
var current_project : Project = Global.current_project
|
|
|
|
if Global.has_focus:
|
|
if !cursor_image_has_changed:
|
|
cursor_image_has_changed = true
|
|
if Global.show_left_tool_icon:
|
|
Global.left_cursor.visible = true
|
|
if Global.show_right_tool_icon:
|
|
Global.right_cursor.visible = true
|
|
else:
|
|
if cursor_image_has_changed:
|
|
cursor_image_has_changed = false
|
|
Global.left_cursor.visible = false
|
|
Global.right_cursor.visible = false
|
|
|
|
Tools.handle_draw(current_pixel.floor(), event)
|
|
|
|
if sprite_changed_this_frame:
|
|
update_texture(current_project.current_layer)
|
|
|
|
|
|
func camera_zoom() -> void:
|
|
# Set camera zoom based on the sprite size
|
|
var bigger_canvas_axis = max(Global.current_project.size.x, Global.current_project.size.y)
|
|
var zoom_max := Vector2(bigger_canvas_axis, bigger_canvas_axis) * 0.01
|
|
var cameras = [Global.camera, Global.camera2, Global.camera_preview]
|
|
for camera in cameras:
|
|
if zoom_max > Vector2.ONE:
|
|
camera.zoom_max = zoom_max
|
|
else:
|
|
camera.zoom_max = Vector2.ONE
|
|
|
|
if camera == Global.camera_preview:
|
|
Global.preview_zoom_slider.max_value = -camera.zoom_min.x
|
|
Global.preview_zoom_slider.min_value = -camera.zoom_max.x
|
|
|
|
camera.fit_to_frame(Global.current_project.size)
|
|
camera.save_values_to_project()
|
|
|
|
Global.transparent_checker._ready() # To update the rect size
|
|
|
|
|
|
func new_empty_frame(first_time := false, single_layer := false, size := Global.current_project.size) -> Frame:
|
|
var frame := Frame.new()
|
|
for l in Global.current_project.layers: # Create as many cels as there are layers
|
|
# The sprite itself
|
|
var sprite := Image.new()
|
|
if first_time:
|
|
if Global.config_cache.has_section_key("preferences", "default_image_width"):
|
|
Global.current_project.size.x = Global.config_cache.get_value("preferences", "default_image_width")
|
|
if Global.config_cache.has_section_key("preferences", "default_image_height"):
|
|
Global.current_project.size.y = Global.config_cache.get_value("preferences", "default_image_height")
|
|
if Global.config_cache.has_section_key("preferences", "default_fill_color"):
|
|
fill_color = Global.config_cache.get_value("preferences", "default_fill_color")
|
|
sprite.create(size.x, size.y, false, Image.FORMAT_RGBA8)
|
|
sprite.fill(fill_color)
|
|
sprite.lock()
|
|
frame.cels.append(Cel.new(sprite, 1))
|
|
|
|
if single_layer:
|
|
break
|
|
|
|
return frame
|
|
|
|
|
|
func handle_undo(action : String, project : Project = Global.current_project, layer_index := -2, frame_index := -2) -> void:
|
|
if !can_undo:
|
|
return
|
|
|
|
if layer_index <= -2:
|
|
layer_index = project.current_layer
|
|
if frame_index <= -2:
|
|
frame_index = project.current_frame
|
|
|
|
var cels := []
|
|
var frames := []
|
|
var layers := []
|
|
if frame_index == -1:
|
|
frames = project.frames
|
|
else:
|
|
frames.append(project.frames[frame_index])
|
|
|
|
if layer_index == -1:
|
|
layers = project.layers
|
|
else:
|
|
layers.append(project.layers[layer_index])
|
|
|
|
for f in frames:
|
|
for l in layers:
|
|
var index = project.layers.find(l)
|
|
cels.append(f.cels[index])
|
|
|
|
project.undos += 1
|
|
project.undo_redo.create_action(action)
|
|
for cel in cels:
|
|
# If we don't unlock the image, it doesn't work properly
|
|
cel.image.unlock()
|
|
var data = cel.image.data
|
|
cel.image.lock()
|
|
project.undo_redo.add_undo_property(cel.image, "data", data)
|
|
project.undo_redo.add_undo_method(Global, "undo", frame_index, layer_index, project)
|
|
|
|
can_undo = false
|
|
|
|
|
|
func handle_redo(_action : String, project : Project = Global.current_project, layer_index := -2, frame_index := -2) -> void:
|
|
can_undo = true
|
|
if project.undos < project.undo_redo.get_version():
|
|
return
|
|
|
|
if layer_index <= -2:
|
|
layer_index = project.current_layer
|
|
if frame_index <= -2:
|
|
frame_index = project.current_frame
|
|
|
|
var cels := []
|
|
var frames := []
|
|
var layers := []
|
|
if frame_index == -1:
|
|
frames = project.frames
|
|
else:
|
|
frames.append(project.frames[frame_index])
|
|
|
|
if layer_index == -1:
|
|
layers = project.layers
|
|
else:
|
|
layers.append(project.layers[layer_index])
|
|
|
|
for f in frames:
|
|
for l in layers:
|
|
var index = project.layers.find(l)
|
|
cels.append(f.cels[index])
|
|
|
|
for cel in cels:
|
|
project.undo_redo.add_do_property(cel.image, "data", cel.image.data)
|
|
project.undo_redo.add_do_method(Global, "redo", frame_index, layer_index, project)
|
|
project.undo_redo.commit_action()
|
|
|
|
|
|
func update_texture(layer_index : int, frame_index := -1, project : Project = Global.current_project) -> void:
|
|
if frame_index == -1:
|
|
frame_index = project.current_frame
|
|
var current_cel : Cel = project.frames[frame_index].cels[layer_index]
|
|
current_cel.image_texture.create_from_image(current_cel.image, 0)
|
|
|
|
if project == Global.current_project:
|
|
var frame_texture_rect : TextureRect
|
|
frame_texture_rect = Global.find_node_by_name(project.layers[layer_index].frame_container.get_child(frame_index), "CelTexture")
|
|
frame_texture_rect.texture = current_cel.image_texture
|
|
|
|
|
|
func onion_skinning() -> void:
|
|
# Past
|
|
if Global.onion_skinning_past_rate > 0:
|
|
var color : Color
|
|
if Global.onion_skinning_blue_red:
|
|
color = Color.blue
|
|
else:
|
|
color = Color.white
|
|
for i in range(1, Global.onion_skinning_past_rate + 1):
|
|
if Global.current_project.current_frame >= i:
|
|
var layer_i := 0
|
|
for layer in Global.current_project.frames[Global.current_project.current_frame - i].cels:
|
|
if Global.current_project.layers[layer_i].visible:
|
|
color.a = 0.6 / i
|
|
draw_texture(layer.image_texture, location, color)
|
|
layer_i += 1
|
|
|
|
# Future
|
|
if Global.onion_skinning_future_rate > 0:
|
|
var color : Color
|
|
if Global.onion_skinning_blue_red:
|
|
color = Color.red
|
|
else:
|
|
color = Color.white
|
|
for i in range(1, Global.onion_skinning_future_rate + 1):
|
|
if Global.current_project.current_frame < Global.current_project.frames.size() - i:
|
|
var layer_i := 0
|
|
for layer in Global.current_project.frames[Global.current_project.current_frame + i].cels:
|
|
if Global.current_project.layers[layer_i].visible:
|
|
color.a = 0.6 / i
|
|
draw_texture(layer.image_texture, location, color)
|
|
layer_i += 1
|