mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-06-25 21:34:43 -04:00
Made a Frame class, no longer use multiple Canvases for multiple frames
The Canvas is now single node, instead of having multiple canvases for each frame. This should also be a performance optimization, since there are less canvas nodes, so there are less _input() calls. It should also fix a rare Undo/Redo issue with motion drawing and making lines. Could be unstable, needs more testing. As a side effect, the guides are now the same for all frames, so this should also close #246.
This commit is contained in:
parent
d8136a3e17
commit
54b628f6cb
17 changed files with 412 additions and 459 deletions
|
@ -35,7 +35,7 @@ var undos := 0 # The number of times we added undo properties
|
|||
var project_has_changed := false # Checks if the user has made changes to the project
|
||||
|
||||
# Canvas related stuff
|
||||
var canvases := [] setget canvases_changed
|
||||
var frames := [] setget frames_changed
|
||||
var layers := [] setget layers_changed
|
||||
var layers_changed_skip := false
|
||||
var current_frame := 0 setget frame_changed
|
||||
|
@ -57,7 +57,6 @@ var animation_tags := [] setget animation_tags_changed
|
|||
var play_only_tags := true
|
||||
|
||||
var theme_type : int = Theme_Types.DARK
|
||||
var is_default_image := true
|
||||
var default_image_width := 64
|
||||
var default_image_height := 64
|
||||
var default_fill_color := Color(0, 0, 0, 0)
|
||||
|
@ -136,7 +135,6 @@ var top_menu_container : Panel
|
|||
var left_cursor : Sprite
|
||||
var right_cursor : Sprite
|
||||
var canvas : Canvas
|
||||
var canvas_parent : Node
|
||||
var main_viewport : ViewportContainer
|
||||
var second_viewport : ViewportContainer
|
||||
var camera : Camera2D
|
||||
|
@ -225,7 +223,6 @@ var edit_palette_popup : WindowDialog
|
|||
var new_palette_dialog : ConfirmationDialog
|
||||
var new_palette_name_line_edit : LineEdit
|
||||
var palette_import_file_dialog : FileDialog
|
||||
|
||||
var error_dialog : AcceptDialog
|
||||
|
||||
onready var current_version : String = ProjectSettings.get_setting("application/config/Version")
|
||||
|
@ -251,15 +248,13 @@ func _ready() -> void:
|
|||
left_cursor = find_node_by_name(root, "LeftCursor")
|
||||
right_cursor = find_node_by_name(root, "RightCursor")
|
||||
canvas = find_node_by_name(root, "Canvas")
|
||||
canvases.append(canvas)
|
||||
left_cursor_tool_texture = ImageTexture.new()
|
||||
left_cursor_tool_texture.create_from_image(preload("res://assets/graphics/cursor_icons/pencil_cursor.png"))
|
||||
right_cursor_tool_texture = ImageTexture.new()
|
||||
right_cursor_tool_texture.create_from_image(preload("res://assets/graphics/cursor_icons/eraser_cursor.png"))
|
||||
canvas_parent = canvas.get_parent()
|
||||
main_viewport = find_node_by_name(root, "ViewportContainer")
|
||||
second_viewport = find_node_by_name(root, "ViewportContainer2")
|
||||
camera = find_node_by_name(canvas_parent, "Camera2D")
|
||||
camera = find_node_by_name(main_viewport, "Camera2D")
|
||||
camera2 = find_node_by_name(root, "Camera2D2")
|
||||
camera_preview = find_node_by_name(root, "CameraPreview")
|
||||
selection_rectangle = find_node_by_name(root, "SelectionRectangle")
|
||||
|
@ -404,33 +399,26 @@ func general_redo() -> void:
|
|||
notification_label("Redo: %s" % action_name)
|
||||
|
||||
|
||||
func undo(_canvases : Array, layer_index : int = -1) -> void:
|
||||
func undo(_frame_index := -1, _layer_index := -1) -> void:
|
||||
general_undo()
|
||||
var action_name := undo_redo.get_current_action_name()
|
||||
if action_name == "Draw" or action_name == "Rectangle Select" or action_name == "Scale" or action_name == "Merge Layer" or action_name == "Link Cel" or action_name == "Unlink Cel":
|
||||
for c in _canvases:
|
||||
if layer_index > -1:
|
||||
c.update_texture(layer_index)
|
||||
else:
|
||||
for i in c.layers.size():
|
||||
c.update_texture(i)
|
||||
if _layer_index > -1 and _frame_index > -1:
|
||||
canvas.update_texture(_layer_index, _frame_index)
|
||||
else:
|
||||
for i in frames.size():
|
||||
for j in layers.size():
|
||||
canvas.update_texture(j, i)
|
||||
|
||||
if action_name == "Scale":
|
||||
c.camera_zoom()
|
||||
if action_name == "Scale":
|
||||
canvas.camera_zoom()
|
||||
|
||||
if action_name == "Add Frame":
|
||||
canvas_parent.remove_child(_canvases[0])
|
||||
# This actually means that canvases.size is one, but it hasn't been updated yet
|
||||
if canvases.size() == 2: # Stop animating
|
||||
elif "Frame" in action_name:
|
||||
# This actually means that frames.size is one, but it hasn't been updated yet
|
||||
if frames.size() == 2: # Stop animating
|
||||
play_forward.pressed = false
|
||||
play_backwards.pressed = false
|
||||
animation_timer.stop()
|
||||
elif action_name == "Remove Frame":
|
||||
canvas_parent.add_child(_canvases[0])
|
||||
canvas_parent.move_child(_canvases[0], _canvases[0].frame)
|
||||
elif action_name == "Change Frame Order":
|
||||
canvas_parent.move_child(_canvases[0], _canvases[0].frame)
|
||||
canvas_parent.move_child(canvas_parent.get_node("TransparentChecker"), 0)
|
||||
|
||||
canvas.update()
|
||||
if !project_has_changed:
|
||||
|
@ -438,31 +426,25 @@ func undo(_canvases : Array, layer_index : int = -1) -> void:
|
|||
self.window_title = window_title + "(*)"
|
||||
|
||||
|
||||
func redo(_canvases : Array, layer_index : int = -1) -> void:
|
||||
func redo(_frame_index := -1, _layer_index := -1) -> void:
|
||||
general_redo()
|
||||
var action_name := undo_redo.get_current_action_name()
|
||||
if action_name == "Draw" or action_name == "Rectangle Select" or action_name == "Scale" or action_name == "Merge Layer" or action_name == "Link Cel" or action_name == "Unlink Cel":
|
||||
for c in _canvases:
|
||||
if layer_index > -1:
|
||||
c.update_texture(layer_index)
|
||||
else:
|
||||
for i in c.layers.size():
|
||||
c.update_texture(i)
|
||||
if _layer_index > -1 and _frame_index > -1:
|
||||
canvas.update_texture(_layer_index, _frame_index)
|
||||
else:
|
||||
for i in frames.size():
|
||||
for j in layers.size():
|
||||
canvas.update_texture(j, i)
|
||||
|
||||
if action_name == "Scale":
|
||||
c.camera_zoom()
|
||||
if action_name == "Scale":
|
||||
canvas.camera_zoom()
|
||||
|
||||
if action_name == "Add Frame":
|
||||
canvas_parent.add_child(_canvases[0])
|
||||
elif action_name == "Remove Frame":
|
||||
canvas_parent.remove_child(_canvases[0])
|
||||
if canvases.size() == 1: # Stop animating
|
||||
elif "Frame" in action_name:
|
||||
if frames.size() == 1: # Stop animating
|
||||
play_forward.pressed = false
|
||||
play_backwards.pressed = false
|
||||
animation_timer.stop()
|
||||
elif action_name == "Change Frame Order":
|
||||
canvas_parent.move_child(_canvases[0], _canvases[0].frame)
|
||||
canvas_parent.move_child(canvas_parent.get_node("TransparentChecker"), 0)
|
||||
|
||||
canvas.update()
|
||||
if !project_has_changed:
|
||||
|
@ -475,8 +457,8 @@ func title_changed(value : String) -> void:
|
|||
OS.set_window_title(value)
|
||||
|
||||
|
||||
func canvases_changed(value : Array) -> void:
|
||||
canvases = value
|
||||
func frames_changed(value : Array) -> void:
|
||||
frames = value
|
||||
for container in frames_container.get_children():
|
||||
for button in container.get_children():
|
||||
container.remove_child(button)
|
||||
|
@ -490,7 +472,7 @@ func canvases_changed(value : Array) -> void:
|
|||
for i in range(layers.size() - 1, -1, -1):
|
||||
frames_container.add_child(layers[i].frame_container)
|
||||
|
||||
for j in range(canvases.size()):
|
||||
for j in range(frames.size()):
|
||||
var label := Label.new()
|
||||
label.rect_min_size.x = 36
|
||||
label.align = Label.ALIGN_CENTER
|
||||
|
@ -501,7 +483,7 @@ func canvases_changed(value : Array) -> void:
|
|||
var cel_button = load("res://src/UI/Timeline/CelButton.tscn").instance()
|
||||
cel_button.frame = j
|
||||
cel_button.layer = i
|
||||
cel_button.get_child(0).texture = Global.canvases[j].layers[i].image_texture
|
||||
cel_button.get_child(0).texture = frames[j].cels[i].image_texture
|
||||
|
||||
layers[i].frame_container.add_child(cel_button)
|
||||
|
||||
|
@ -509,19 +491,16 @@ func canvases_changed(value : Array) -> void:
|
|||
# otherwise, this code is useless in this context, since these values are being set
|
||||
# when the play buttons get pressed, anyway
|
||||
animation_timeline.first_frame = 0
|
||||
animation_timeline.last_frame = canvases.size() - 1
|
||||
animation_timeline.last_frame = frames.size() - 1
|
||||
if play_only_tags:
|
||||
for tag in animation_tags:
|
||||
if current_frame + 1 >= tag.from && current_frame + 1 <= tag.to:
|
||||
animation_timeline.first_frame = tag.from - 1
|
||||
animation_timeline.last_frame = min(canvases.size() - 1, tag.to - 1)
|
||||
animation_timeline.last_frame = min(frames.size() - 1, tag.to - 1)
|
||||
|
||||
|
||||
func clear_canvases() -> void:
|
||||
for child in canvas_parent.get_children():
|
||||
if child is Canvas:
|
||||
child.queue_free()
|
||||
canvases.clear()
|
||||
func clear_frames() -> void:
|
||||
frames.clear()
|
||||
animation_tags.clear()
|
||||
self.animation_tags = animation_tags # To execute animation_tags_changed()
|
||||
|
||||
|
@ -564,11 +543,11 @@ func layers_changed(value : Array) -> void:
|
|||
layer_container.line_edit.text = layers[i].name
|
||||
|
||||
frames_container.add_child(layers[i].frame_container)
|
||||
for j in range(canvases.size()):
|
||||
for j in range(frames.size()):
|
||||
var cel_button = load("res://src/UI/Timeline/CelButton.tscn").instance()
|
||||
cel_button.frame = j
|
||||
cel_button.layer = i
|
||||
cel_button.get_child(0).texture = Global.canvases[j].layers[i].image_texture
|
||||
cel_button.get_child(0).texture = frames[j].cels[i].image_texture
|
||||
|
||||
layers[i].frame_container.add_child(cel_button)
|
||||
|
||||
|
@ -590,13 +569,9 @@ func layers_changed(value : Array) -> void:
|
|||
|
||||
func frame_changed(value : int) -> void:
|
||||
current_frame = value
|
||||
current_frame_mark_label.text = "%s/%s" % [str(current_frame + 1), canvases.size()]
|
||||
current_frame_mark_label.text = "%s/%s" % [str(current_frame + 1), frames.size()]
|
||||
|
||||
var i := 0
|
||||
for c in canvases: # De-select all the other canvases/frames
|
||||
c.visible = false
|
||||
c.is_making_line = false
|
||||
c.line_2d.set_point_position(1, c.line_2d.points[0])
|
||||
for i in frames.size(): # De-select all the other frames
|
||||
var text_color := Color.white
|
||||
if theme_type == Theme_Types.CARAMEL || theme_type == Theme_Types.LIGHT:
|
||||
text_color = Color.black
|
||||
|
@ -604,27 +579,26 @@ func frame_changed(value : int) -> void:
|
|||
for layer in layers:
|
||||
if i < layer.frame_container.get_child_count():
|
||||
layer.frame_container.get_child(i).pressed = false
|
||||
i += 1
|
||||
|
||||
# Select the new canvas/frame
|
||||
canvas = canvases[current_frame]
|
||||
canvas.visible = true
|
||||
# Select the new frame
|
||||
frame_ids.get_child(current_frame).add_color_override("font_color", control.theme.get_color("Selected Color", "Label"))
|
||||
if current_frame < layers[current_layer].frame_container.get_child_count():
|
||||
layers[current_layer].frame_container.get_child(current_frame).pressed = true
|
||||
|
||||
if canvases.size() == 1:
|
||||
if frames.size() == 1:
|
||||
disable_button(remove_frame_button, true)
|
||||
elif !layers[current_layer].locked:
|
||||
disable_button(remove_frame_button, false)
|
||||
|
||||
Global.canvas.update()
|
||||
Global.transparent_checker._ready() # To update the rect size
|
||||
|
||||
|
||||
func layer_changed(value : int) -> void:
|
||||
current_layer = value
|
||||
layer_opacity_slider.value = canvas.layers[current_layer].opacity * 100
|
||||
layer_opacity_spinbox.value = canvas.layers[current_layer].opacity * 100
|
||||
if current_frame < frames.size():
|
||||
layer_opacity_slider.value = frames[current_frame].cels[current_layer].opacity * 100
|
||||
layer_opacity_spinbox.value = frames[current_frame].cels[current_layer].opacity * 100
|
||||
|
||||
for container in layers_container.get_children():
|
||||
container.pressed = false
|
||||
|
@ -719,12 +693,12 @@ func animation_tags_changed(value : Array) -> void:
|
|||
# otherwise, this code is useless in this context, since these values are being set
|
||||
# when the play buttons get pressed, anyway
|
||||
animation_timeline.first_frame = 0
|
||||
animation_timeline.last_frame = canvases.size() - 1
|
||||
animation_timeline.last_frame = frames.size() - 1
|
||||
if play_only_tags:
|
||||
for tag in animation_tags:
|
||||
if current_frame + 1 >= tag.from && current_frame + 1 <= tag.to:
|
||||
animation_timeline.first_frame = tag.from - 1
|
||||
animation_timeline.last_frame = min(canvases.size() - 1, tag.to - 1)
|
||||
animation_timeline.last_frame = min(frames.size() - 1, tag.to - 1)
|
||||
|
||||
|
||||
func update_hint_tooltips() -> void:
|
||||
|
|
|
@ -36,17 +36,22 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
# and the status would return "beta"
|
||||
var file_major_version = int(file_ver_splitted_numbers[0].replace("v", ""))
|
||||
var file_minor_version = int(file_ver_splitted_numbers[1])
|
||||
var _file_patch_version := 0
|
||||
var file_patch_version := 0
|
||||
var _file_status_version : String
|
||||
|
||||
if file_ver_splitted_numbers.size() > 2:
|
||||
_file_patch_version = int(file_ver_splitted_numbers[2])
|
||||
file_patch_version = int(file_ver_splitted_numbers[2])
|
||||
if file_ver_splitted.size() > 1:
|
||||
_file_status_version = file_ver_splitted[1]
|
||||
|
||||
if file_major_version == 0 and file_minor_version < 5:
|
||||
Global.notification_label("File is from an older version of Pixelorama, as such it might not work properly")
|
||||
|
||||
var new_guides := true
|
||||
if file_major_version == 0:
|
||||
if file_minor_version < 7 or (file_minor_version == 7 and file_patch_version == 0):
|
||||
new_guides = false
|
||||
|
||||
var frame := 0
|
||||
Global.layers.clear()
|
||||
|
||||
|
@ -65,10 +70,9 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
global_layer_line = file.get_line()
|
||||
|
||||
var frame_line := file.get_line()
|
||||
Global.clear_canvases()
|
||||
Global.clear_frames()
|
||||
while frame_line == "--": # Load frames
|
||||
var canvas : Canvas = load("res://src/Canvas.tscn").instance()
|
||||
Global.canvas = canvas
|
||||
var frame_class := Frame.new()
|
||||
var width := file.get_16()
|
||||
var height := file.get_16()
|
||||
|
||||
|
@ -81,24 +85,49 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
if frame == 0:
|
||||
var l := Layer.new(layer_name_old_version)
|
||||
Global.layers.append(l)
|
||||
var layer_transparency := 1.0
|
||||
var cel_opacity := 1.0
|
||||
if file_major_version >= 0 and file_minor_version > 5:
|
||||
layer_transparency = file.get_float()
|
||||
cel_opacity = file.get_float()
|
||||
var image := Image.new()
|
||||
image.create_from_data(width, height, false, Image.FORMAT_RGBA8, buffer)
|
||||
image.lock()
|
||||
canvas.layers.append(Cel.new(image, layer_transparency))
|
||||
frame_class.cels.append(Cel.new(image, cel_opacity))
|
||||
if file_major_version >= 0 and file_minor_version >= 7:
|
||||
if frame in linked_cels[layer_i]:
|
||||
Global.layers[layer_i].linked_cels.append(canvas)
|
||||
Global.layers[layer_i].linked_cels.append(frame_class)
|
||||
|
||||
layer_i += 1
|
||||
layer_line = file.get_line()
|
||||
|
||||
if !new_guides:
|
||||
var guide_line := file.get_line() # "guideline" no pun intended
|
||||
while guide_line == "|": # Load guides
|
||||
var guide := Guide.new()
|
||||
guide.type = file.get_8()
|
||||
if guide.type == guide.Types.HORIZONTAL:
|
||||
guide.add_point(Vector2(-99999, file.get_16()))
|
||||
guide.add_point(Vector2(99999, file.get_16()))
|
||||
else:
|
||||
guide.add_point(Vector2(file.get_16(), -99999))
|
||||
guide.add_point(Vector2(file.get_16(), 99999))
|
||||
guide.has_focus = false
|
||||
Global.canvas.add_child(guide)
|
||||
guide_line = file.get_line()
|
||||
|
||||
Global.canvas.size = Vector2(width, height)
|
||||
Global.frames.append(frame_class)
|
||||
frame_line = file.get_line()
|
||||
frame += 1
|
||||
|
||||
Global.frames = Global.frames # Just to call Global.frames_changed
|
||||
Global.current_layer = Global.layers.size() - 1
|
||||
Global.current_frame = frame - 1
|
||||
Global.layers = Global.layers # Just to call Global.layers_changed
|
||||
|
||||
if new_guides:
|
||||
var guide_line := file.get_line() # "guideline" no pun intended
|
||||
while guide_line == "|": # Load guides
|
||||
var guide := Guide.new()
|
||||
guide.default_color = Color.purple
|
||||
guide.type = file.get_8()
|
||||
if guide.type == guide.Types.HORIZONTAL:
|
||||
guide.add_point(Vector2(-99999, file.get_16()))
|
||||
|
@ -107,20 +136,9 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
guide.add_point(Vector2(file.get_16(), -99999))
|
||||
guide.add_point(Vector2(file.get_16(), 99999))
|
||||
guide.has_focus = false
|
||||
canvas.add_child(guide)
|
||||
Global.canvas.add_child(guide)
|
||||
guide_line = file.get_line()
|
||||
|
||||
canvas.size = Vector2(width, height)
|
||||
Global.canvases.append(canvas)
|
||||
canvas.frame = frame
|
||||
Global.canvas_parent.add_child(canvas)
|
||||
frame_line = file.get_line()
|
||||
frame += 1
|
||||
|
||||
Global.canvases = Global.canvases # Just to call Global.canvases_changed
|
||||
Global.current_layer = Global.layers.size() - 1
|
||||
Global.current_frame = frame - 1
|
||||
Global.layers = Global.layers # Just to call Global.layers_changed
|
||||
# Load tool options
|
||||
Global.color_pickers[0].color = file.get_var()
|
||||
Global.color_pickers[1].color = file.get_var()
|
||||
|
@ -163,6 +181,7 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
|
|||
tag_line = file.get_line()
|
||||
|
||||
file.close()
|
||||
Global.canvas.camera_zoom()
|
||||
|
||||
if not untitled_backup:
|
||||
# Untitled backup should not change window title and save path
|
||||
|
@ -186,37 +205,38 @@ func save_pxo_file(path : String, autosave : bool) -> void:
|
|||
file.store_8(layer.locked)
|
||||
file.store_8(layer.new_cels_linked)
|
||||
var linked_cels := []
|
||||
for canvas in layer.linked_cels:
|
||||
linked_cels.append(canvas.frame)
|
||||
for frame in layer.linked_cels:
|
||||
linked_cels.append(Global.frames.find(frame))
|
||||
file.store_var(linked_cels) # Linked cels as cel numbers
|
||||
|
||||
file.store_line("END_GLOBAL_LAYERS")
|
||||
|
||||
# Store frames
|
||||
for canvas in Global.canvases:
|
||||
for frame in Global.frames:
|
||||
file.store_line("--")
|
||||
file.store_16(canvas.size.x)
|
||||
file.store_16(canvas.size.y)
|
||||
for layer in canvas.layers: # Store canvas layers
|
||||
file.store_16(Global.canvas.size.x)
|
||||
file.store_16(Global.canvas.size.y)
|
||||
for cel in frame.cels: # Store canvas layers
|
||||
file.store_line("-")
|
||||
file.store_buffer(layer.image.get_data())
|
||||
file.store_float(layer.opacity)
|
||||
file.store_buffer(cel.image.get_data())
|
||||
file.store_float(cel.opacity)
|
||||
file.store_line("END_LAYERS")
|
||||
|
||||
# Store guides
|
||||
for child in canvas.get_children():
|
||||
if child is Guide:
|
||||
file.store_line("|")
|
||||
file.store_8(child.type)
|
||||
if child.type == child.Types.HORIZONTAL:
|
||||
file.store_16(child.points[0].y)
|
||||
file.store_16(child.points[1].y)
|
||||
else:
|
||||
file.store_16(child.points[1].x)
|
||||
file.store_16(child.points[0].x)
|
||||
file.store_line("END_GUIDES")
|
||||
file.store_line("END_FRAMES")
|
||||
|
||||
# Store guides
|
||||
for child in Global.canvas.get_children():
|
||||
if child is Guide:
|
||||
file.store_line("|")
|
||||
file.store_8(child.type)
|
||||
if child.type == child.Types.HORIZONTAL:
|
||||
file.store_16(child.points[0].y)
|
||||
file.store_16(child.points[1].y)
|
||||
else:
|
||||
file.store_16(child.points[1].x)
|
||||
file.store_16(child.points[0].x)
|
||||
file.store_line("END_GUIDES")
|
||||
|
||||
# Save tool options
|
||||
var left_color : Color = Global.color_pickers[0].color
|
||||
var right_color : Color = Global.color_pickers[1].color
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue