1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-20 20:43:14 +00:00

Merge pull request #256 from Orama-Interactive/refactoring

Bring more refactoring changes to master
This commit is contained in:
Manolis Papadeas 2020-06-04 20:32:23 +03:00 committed by GitHub
commit fcdbb07b92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 555 additions and 617 deletions

View file

@ -6,8 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [v0.7.1] - Unreleased
This update has been brought to you by the contributions of:
Igor Santarek (jegor377), rob-a-bolton
Igor Santarek (jegor377), rob-a-bolton, Kinwailo
### Added
- Ability to remove the current palette.
@ -17,11 +16,14 @@ Igor Santarek (jegor377), rob-a-bolton
- Drawing is no longer limited by the canvas boundaries. This means that, if you have a brush largen than 1px, you can draw on the edges of the canvas. All pixels that are being drawn outside of the canvas will still have no effect.
- Language and theme checkboxes are now radio buttons.
- The Blue theme has more similar margins and seperations with the rest of the themes.
- The guides are now the same for all frames.
- Pixel perfect is no longer enabled when the brush size is bigger than 1px.
### Fixed
- Fixed failed imports of gpl palettes by adding support for the newer variant of gpl files. ([#250](https://github.com/Orama-Interactive/Pixelorama/pull/250))
- Fixed alpha blending and lighting/darkening issues when drawing pixels with mirroring.
- Fixed issue where if you moved a frame to the start (move left), it was invisible.
- Fixed a rare issue with Undo/Redo not working while motion drawing and making lines.
<br><br>
## [v0.7] - 2020-05-16

View file

@ -24,6 +24,16 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/Classes/Cel.gd"
}, {
"base": "Reference",
"class": "Drawer",
"language": "GDScript",
"path": "res://src/Classes/Drawers.gd"
}, {
"base": "Reference",
"class": "Frame",
"language": "GDScript",
"path": "res://src/Classes/Frame.gd"
}, {
"base": "Line2D",
"class": "Guide",
"language": "GDScript",
@ -53,6 +63,8 @@ _global_script_class_icons={
"AnimationTag": "",
"Canvas": "",
"Cel": "",
"Drawer": "",
"Frame": "",
"Guide": "",
"Layer": "",
"LayerButton": "",
@ -71,7 +83,7 @@ boot_splash/bg_color=Color( 0.145098, 0.145098, 0.164706, 1 )
config/icon="res://assets/graphics/icons/icon.png"
config/macos_native_icon="res://assets/graphics/icons/icon.icns"
config/windows_native_icon="res://assets/graphics/icons/icon.ico"
config/Version="v0.7"
config/Version="v0.7.1"
[autoload]

View file

@ -1,26 +1,23 @@
extends Node
const Drawer = preload("res://src/Classes/Drawers.gd").Drawer
const SimpleDrawer = preload("res://src/Classes/Drawers.gd").SimpleDrawer
const PixelPerfectDrawer = preload("res://src/Classes/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()
var drawer := Drawer.new()
var mouse_press_pixels := [] # Cleared after mouse release
var mouse_press_pressure_values := [] # Cleared after mouse release
func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color, pen_pressure : float, current_mouse_button := -1, current_action := -1, drawer : Drawer = simple_drawer) -> void:
var west_limit = Global.canvas.west_limit
var east_limit = Global.canvas.east_limit
var north_limit = Global.canvas.north_limit
var south_limit = Global.canvas.south_limit
if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
func reset() -> void:
drawer.reset()
mouse_press_pixels.clear()
mouse_press_pressure_values.clear()
func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color, pen_pressure : float, current_mouse_button := -1, current_action := -1) -> void:
var x_min = Global.canvas.x_min
var x_max = Global.canvas.x_max
var y_min = Global.canvas.y_min
var y_max = Global.canvas.y_max
if !point_in_rectangle(pos, Vector2(x_min - 1, y_min - 1), Vector2(x_max, y_max)):
return
var pos_floored := pos.floor()
@ -48,10 +45,10 @@ func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color, pen_pressu
func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_button : int, pen_pressure : float, current_action := -1) -> void:
if Global.can_draw && Global.has_focus:
var west_limit = Global.canvas.west_limit
var east_limit = Global.canvas.east_limit
var north_limit = Global.canvas.north_limit
var south_limit = Global.canvas.south_limit
var x_min = Global.canvas.x_min
var x_max = Global.canvas.x_max
var y_min = Global.canvas.y_min
var y_max = Global.canvas.y_max
if Global.pressure_sensitivity_mode == Global.Pressure_Sensitivity.ALPHA:
if current_action == Global.Tools.PENCIL:
@ -64,6 +61,11 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt
var horizontal_mirror : bool = Global.horizontal_mirror[current_mouse_button]
var vertical_mirror : bool = Global.vertical_mirror[current_mouse_button]
var pixel_perfect : bool = Global.pixel_perfect[current_mouse_button]
drawer.pixel_perfect = pixel_perfect if brush_size == 1 else false
drawer.h_mirror = horizontal_mirror
drawer.v_mirror = vertical_mirror
if brush_type == Global.Brush_Types.PIXEL || current_action == Global.Tools.LIGHTENDARKEN:
var start_pos_x = pos.x - (brush_size >> 1)
@ -73,42 +75,11 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt
for cur_pos_x in range(start_pos_x, end_pos_x):
for cur_pos_y in range(start_pos_y, end_pos_y):
var pixel_perfect : bool = Global.pixel_perfect[current_mouse_button]
# warning-ignore:incompatible_ternary
var drawer : Drawer = pixel_perfect_drawer if pixel_perfect else simple_drawer
draw_pixel_blended(sprite, Vector2(cur_pos_x, cur_pos_y), color, pen_pressure, current_mouse_button, current_action, drawer)
# Handle mirroring
var mirror_x = east_limit + west_limit - cur_pos_x - 1
var mirror_y = south_limit + north_limit - cur_pos_y - 1
if horizontal_mirror:
# warning-ignore:incompatible_ternary
var drawer_h_mirror : Drawer = pixel_perfect_drawer_h_mirror if pixel_perfect else simple_drawer
draw_pixel_blended(sprite, Vector2(mirror_x, cur_pos_y), color, pen_pressure, current_mouse_button, current_action, drawer_h_mirror)
if vertical_mirror:
# warning-ignore:incompatible_ternary
var drawer_v_mirror : Drawer = pixel_perfect_drawer_v_mirror if pixel_perfect else simple_drawer
draw_pixel_blended(sprite, Vector2(cur_pos_x, mirror_y), color, pen_pressure, current_mouse_button, current_action, drawer_v_mirror)
if horizontal_mirror && vertical_mirror:
# warning-ignore:incompatible_ternary
var drawer_hv_mirror : Drawer = pixel_perfect_drawer_hv_mirror if pixel_perfect else simple_drawer
draw_pixel_blended(sprite, Vector2(mirror_x, mirror_y), color, pen_pressure, current_mouse_button, current_action, drawer_hv_mirror)
draw_pixel_blended(sprite, Vector2(cur_pos_x, cur_pos_y), color, pen_pressure, current_mouse_button, current_action)
Global.canvas.sprite_changed_this_frame = true
elif brush_type == Global.Brush_Types.CIRCLE || brush_type == Global.Brush_Types.FILLED_CIRCLE:
plot_circle(sprite, pos.x, pos.y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
# Handle mirroring
var mirror_x = east_limit + west_limit - pos.x
var mirror_y = south_limit + north_limit - pos.y
if horizontal_mirror:
plot_circle(sprite, mirror_x, pos.y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
if vertical_mirror:
plot_circle(sprite, pos.x, mirror_y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
if horizontal_mirror && vertical_mirror:
plot_circle(sprite, mirror_x, mirror_y, brush_size, color, brush_type == Global.Brush_Types.FILLED_CIRCLE)
Global.canvas.sprite_changed_this_frame = true
else:
@ -136,8 +107,8 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt
# The selection rectangle
# If there's no rectangle, the whole canvas is considered a selection
var selection_rect := Rect2()
selection_rect.position = Vector2(west_limit, north_limit)
selection_rect.end = Vector2(east_limit, south_limit)
selection_rect.position = Vector2(x_min, y_min)
selection_rect.end = Vector2(x_max, y_max)
# Intersection of the position rectangle and selection
var pos_rect_clipped := pos_rect.clip(selection_rect)
# If the size is 0, that means that the brush wasn't positioned inside the selection
@ -156,8 +127,8 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt
src_rect.size.y = min(src_rect.size.y, selection_rect.size.y)
# Handle mirroring
var mirror_x = east_limit + west_limit - pos.x - (pos.x - dst.x)
var mirror_y = south_limit + north_limit - pos.y - (pos.y - dst.y)
var mirror_x = x_max + x_min - pos.x - (pos.x - dst.x)
var mirror_y = y_max + y_min - pos.y - (pos.y - dst.y)
if int(pos_rect_clipped.size.x) % 2 != 0:
mirror_x -= 1
if int(pos_rect_clipped.size.y) % 2 != 0:
@ -255,10 +226,10 @@ func plot_circle(sprite : Image, xm : int, ym : int, r : int, color : Color, fil
# Thanks to https://en.wikipedia.org/wiki/Flood_fill
func flood_fill(sprite : Image, pos : Vector2, target_color : Color, replace_color : Color) -> void:
var west_limit = Global.canvas.west_limit
var east_limit = Global.canvas.east_limit
var north_limit = Global.canvas.north_limit
var south_limit = Global.canvas.south_limit
var x_min = Global.canvas.x_min
var x_max = Global.canvas.x_max
var y_min = Global.canvas.y_min
var y_max = Global.canvas.y_max
pos = pos.floor()
var pixel = sprite.get_pixelv(pos)
if target_color == replace_color:
@ -267,7 +238,7 @@ func flood_fill(sprite : Image, pos : Vector2, target_color : Color, replace_col
return
else:
if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
if !point_in_rectangle(pos, Vector2(x_min - 1, y_min - 1), Vector2(x_max, y_max)):
return
var q = [pos]
@ -277,9 +248,9 @@ func flood_fill(sprite : Image, pos : Vector2, target_color : Color, replace_col
break
var west : Vector2 = n
var east : Vector2 = n
while west.x >= west_limit && sprite.get_pixelv(west) == target_color:
while west.x >= x_min && sprite.get_pixelv(west) == target_color:
west += Vector2.LEFT
while east.x < east_limit && sprite.get_pixelv(east) == target_color:
while east.x < x_max && sprite.get_pixelv(east) == target_color:
east += Vector2.RIGHT
for px in range(west.x + 1, east.x):
var p := Vector2(px, n.y)
@ -288,21 +259,21 @@ func flood_fill(sprite : Image, pos : Vector2, target_color : Color, replace_col
replace_color = sprite.get_pixelv(p)
var north := p + Vector2.UP
var south := p + Vector2.DOWN
if north.y >= north_limit && sprite.get_pixelv(north) == target_color:
if north.y >= y_min && sprite.get_pixelv(north) == target_color:
q.append(north)
if south.y < south_limit && sprite.get_pixelv(south) == target_color:
if south.y < y_max && sprite.get_pixelv(south) == target_color:
q.append(south)
Global.canvas.sprite_changed_this_frame = true
func pattern_fill(sprite : Image, pos : Vector2, pattern : Image, target_color : Color, var offset : Vector2) -> void:
var west_limit = Global.canvas.west_limit
var east_limit = Global.canvas.east_limit
var north_limit = Global.canvas.north_limit
var south_limit = Global.canvas.south_limit
var x_min = Global.canvas.x_min
var x_max = Global.canvas.x_max
var y_min = Global.canvas.y_min
var y_max = Global.canvas.y_max
pos = pos.floor()
if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)):
if !point_in_rectangle(pos, Vector2(x_min - 1, y_min - 1), Vector2(x_max, y_max)):
return
pattern.lock()
@ -312,9 +283,9 @@ func pattern_fill(sprite : Image, pos : Vector2, pattern : Image, target_color :
for n in q:
var west : Vector2 = n
var east : Vector2 = n
while west.x >= west_limit && sprite.get_pixelv(west) == target_color:
while west.x >= x_min && sprite.get_pixelv(west) == target_color:
west += Vector2.LEFT
while east.x < east_limit && sprite.get_pixelv(east) == target_color:
while east.x < x_max && sprite.get_pixelv(east) == target_color:
east += Vector2.RIGHT
for px in range(west.x + 1, east.x):
@ -328,9 +299,9 @@ func pattern_fill(sprite : Image, pos : Vector2, pattern : Image, target_color :
var north := p + Vector2.UP
var south := p + Vector2.DOWN
if north.y >= north_limit && sprite.get_pixelv(north) == target_color:
if north.y >= y_min && sprite.get_pixelv(north) == target_color:
q.append(north)
if south.y < south_limit && sprite.get_pixelv(south) == target_color:
if south.y < y_max && sprite.get_pixelv(south) == target_color:
q.append(south)
pattern.unlock()
@ -587,16 +558,16 @@ func colorDistance(c1 : Color, c2 : Color) -> float:
func adjust_hsv(img: Image, id : int, delta : float) -> void:
var west_limit = Global.canvas.west_limit
var east_limit = Global.canvas.east_limit
var north_limit = Global.canvas.north_limit
var south_limit = Global.canvas.south_limit
var x_min = Global.canvas.x_min
var x_max = Global.canvas.x_max
var y_min = Global.canvas.y_min
var y_max = Global.canvas.y_max
img.lock()
match id:
0: # Hue
for i in range(west_limit, east_limit):
for j in range(north_limit, south_limit):
for i in range(x_min, x_max):
for j in range(y_min, y_max):
var c : Color = img.get_pixel(i,j)
var hue = range_lerp(c.h,0,1,-180,180)
hue = hue + delta
@ -609,8 +580,8 @@ func adjust_hsv(img: Image, id : int, delta : float) -> void:
img.set_pixel(i,j,c)
1: # Saturation
for i in range(west_limit, east_limit):
for j in range(north_limit, south_limit):
for i in range(x_min, x_max):
for j in range(y_min, y_max):
var c : Color = img.get_pixel(i,j)
var sat = c.s
if delta > 0:
@ -621,8 +592,8 @@ func adjust_hsv(img: Image, id : int, delta : float) -> void:
img.set_pixel(i,j,c)
2: # Value
for i in range(west_limit, east_limit):
for j in range(north_limit, south_limit):
for i in range(x_min, x_max):
for j in range(y_min, y_max):
var c : Color = img.get_pixel(i,j)
var val = c.v
if delta > 0:

View file

@ -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:

View file

@ -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

View file

@ -2,22 +2,19 @@ class_name Canvas
extends Node2D
var layers := []
var current_layer_index := 0
var location := Vector2.ZERO
var size := Vector2(64, 64)
var fill_color := Color(0, 0, 0, 0)
var frame := 0
var current_pixel := Vector2.ZERO # pretty much same as mouse_pos, but can be accessed externally
var previous_mouse_pos := Vector2.ZERO
var previous_mouse_pos_for_lines := Vector2.ZERO
var can_undo := true
var cursor_image_has_changed := false
var previous_action := -1
var west_limit := location.x
var east_limit := location.x + size.x
var north_limit := location.y
var south_limit := location.y + size.y
var x_min := location.x
var x_max := location.x + size.x
var y_min := location.y
var y_max := location.y + size.y
var sprite_changed_this_frame := false # for optimization purposes
var is_making_line := false
var made_line := false
@ -28,38 +25,9 @@ var pen_pressure := 1.0 # For tablet pressure sensitivity
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
var fill_layers := layers.empty()
var layer_i := 0
for l in Global.layers:
if fill_layers:
# The sprite itself
var sprite := Image.new()
if Global.is_default_image:
if Global.config_cache.has_section_key("preferences", "default_width"):
size.x = Global.config_cache.get_value("preferences", "default_width")
if Global.config_cache.has_section_key("preferences", "default_height"):
size.y = Global.config_cache.get_value("preferences", "default_height")
if Global.config_cache.has_section_key("preferences", "default_fill_color"):
fill_color = Global.config_cache.get_value("preferences", "default_fill_color")
Global.is_default_image = !Global.is_default_image
sprite.create(size.x, size.y, false, Image.FORMAT_RGBA8)
sprite.fill(fill_color)
sprite.lock()
layers.append(Cel.new(sprite, 1.0))
if self in l.linked_cels:
# If the linked button is pressed, set as the Image & ImageTexture
# to be the same as the first linked cel
layers[layer_i].image = l.linked_cels[0].layers[layer_i].image
layers[layer_i].image_texture = l.linked_cels[0].layers[layer_i].image_texture
layer_i += 1
# Only handle camera zoom settings & offset on the first frame
if Global.canvases[0] == self:
camera_zoom()
var frame : Frame = new_empty_frame(true)
Global.frames.append(frame)
camera_zoom()
line_2d = Line2D.new()
line_2d.width = 0.5
@ -70,24 +38,25 @@ func _ready() -> void:
func _draw() -> void:
var current_cels : Array = Global.frames[Global.current_frame].cels
if Global.onion_skinning:
onion_skinning()
# Draw current frame layers
for i in range(layers.size()):
var modulate_color := Color(1, 1, 1, layers[i].opacity)
for i in range(Global.layers.size()):
var modulate_color := Color(1, 1, 1, current_cels[i].opacity)
if Global.layers[i].visible: # if it's visible
draw_texture(layers[i].image_texture, location, modulate_color)
draw_texture(current_cels[i].image_texture, location, modulate_color)
if Global.tile_mode:
draw_texture(layers[i].image_texture, Vector2(location.x, location.y + size.y), modulate_color) # Down
draw_texture(layers[i].image_texture, Vector2(location.x - size.x, location.y + size.y), modulate_color) # Down Left
draw_texture(layers[i].image_texture, Vector2(location.x - size.x, location.y), modulate_color) # Left
draw_texture(layers[i].image_texture, location - size, modulate_color) # Up left
draw_texture(layers[i].image_texture, Vector2(location.x, location.y - size.y), modulate_color) # Up
draw_texture(layers[i].image_texture, Vector2(location.x + size.x, location.y - size.y), modulate_color) # Up right
draw_texture(layers[i].image_texture, Vector2(location.x + size.x, location.y), modulate_color) # Right
draw_texture(layers[i].image_texture, location + size, modulate_color) # Down right
draw_texture(current_cels[i].image_texture, Vector2(location.x, location.y + size.y), modulate_color) # Down
draw_texture(current_cels[i].image_texture, Vector2(location.x - size.x, location.y + size.y), modulate_color) # Down Left
draw_texture(current_cels[i].image_texture, Vector2(location.x - size.x, location.y), modulate_color) # Left
draw_texture(current_cels[i].image_texture, location - size, modulate_color) # Up left
draw_texture(current_cels[i].image_texture, Vector2(location.x, location.y - size.y), modulate_color) # Up
draw_texture(current_cels[i].image_texture, Vector2(location.x + size.x, location.y - size.y), modulate_color) # Up right
draw_texture(current_cels[i].image_texture, Vector2(location.x + size.x, location.y), modulate_color) # Right
draw_texture(current_cels[i].image_texture, location + size, modulate_color) # Down right
if Global.draw_grid:
draw_grid(Global.grid_type)
@ -130,20 +99,10 @@ func _input(event : InputEvent) -> void:
if (Input.is_action_just_released("left_mouse") && !Input.is_action_pressed("right_mouse")) || (Input.is_action_just_released("right_mouse") && !Input.is_action_pressed("left_mouse")):
made_line = false
DrawingAlgos.mouse_press_pixels.clear()
DrawingAlgos.mouse_press_pressure_values.clear()
DrawingAlgos.pixel_perfect_drawer.reset()
DrawingAlgos.pixel_perfect_drawer_h_mirror.reset()
DrawingAlgos.pixel_perfect_drawer_v_mirror.reset()
DrawingAlgos.pixel_perfect_drawer_hv_mirror.reset()
DrawingAlgos.reset()
can_undo = true
current_pixel = get_local_mouse_position() + location
if Global.current_frame != frame || Global.layers[Global.current_layer].locked:
previous_mouse_pos = current_pixel
previous_mouse_pos.x = clamp(previous_mouse_pos.x, location.x, location.x + size.x)
previous_mouse_pos.y = clamp(previous_mouse_pos.y, location.y, location.y + size.y)
return
if Global.has_focus:
update()
@ -162,15 +121,15 @@ func _input(event : InputEvent) -> void:
var mouse_pos_floored := mouse_pos.floor()
var current_mouse_button := -1
west_limit = location.x
east_limit = location.x + size.x
north_limit = location.y
south_limit = location.y + size.y
x_min = location.x
x_max = location.x + size.x
y_min = location.y
y_max = location.y + size.y
if Global.selected_pixels.size() != 0:
west_limit = max(west_limit, Global.selection_rectangle.polygon[0].x)
east_limit = min(east_limit, Global.selection_rectangle.polygon[2].x)
north_limit = max(north_limit, Global.selection_rectangle.polygon[0].y)
south_limit = min(south_limit, Global.selection_rectangle.polygon[2].y)
x_min = max(x_min, Global.selection_rectangle.polygon[0].x)
x_max = min(x_max, Global.selection_rectangle.polygon[2].x)
y_min = max(y_min, Global.selection_rectangle.polygon[0].y)
y_max = min(y_max, Global.selection_rectangle.polygon[2].y)
if Input.is_mouse_button_pressed(BUTTON_LEFT):
current_mouse_button = Global.Mouse_Button.LEFT
@ -290,8 +249,30 @@ func camera_zoom() -> void:
Global.transparent_checker._ready() # To update the rect size
func new_empty_frame(first_time := false) -> Frame:
var frame := Frame.new()
for l in Global.layers:
# The sprite itself
var sprite := Image.new()
if first_time:
if Global.config_cache.has_section_key("preferences", "default_width"):
size.x = Global.config_cache.get_value("preferences", "default_width")
if Global.config_cache.has_section_key("preferences", "default_height"):
size.y = Global.config_cache.get_value("preferences", "default_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))
return frame
func handle_tools(current_mouse_button : int, current_action : int, mouse_pos : Vector2, can_handle : bool) -> void:
var sprite : Image = layers[Global.current_layer].image
var current_cel : Cel = Global.frames[Global.current_frame].cels[Global.current_layer]
var sprite : Image = current_cel.image
var mouse_pos_floored := mouse_pos.floor()
var mouse_pos_ceiled := mouse_pos.ceil()
@ -314,8 +295,8 @@ func handle_tools(current_mouse_button : int, current_action : int, mouse_pos :
var pattern_offset : Vector2 = Global.fill_pattern_offsets[current_mouse_button]
if fill_area == Global.Fill_Area.SAME_COLOR_AREA: # Paint the specific area of the same color
var mirror_x := east_limit + west_limit - mouse_pos_floored.x - 1
var mirror_y := south_limit + north_limit - mouse_pos_floored.y - 1
var mirror_x := x_max + x_min - mouse_pos_floored.x - 1
var mirror_y := y_max + y_min - mouse_pos_floored.y - 1
var horizontal_mirror : bool = Global.horizontal_mirror[current_mouse_button]
var vertical_mirror : bool = Global.vertical_mirror[current_mouse_button]
@ -345,8 +326,8 @@ func handle_tools(current_mouse_button : int, current_action : int, mouse_pos :
else: # Paint all pixels of the same color
var pixel_color : Color = sprite.get_pixelv(mouse_pos)
for xx in range(west_limit, east_limit):
for yy in range(north_limit, south_limit):
for xx in range(x_min, x_max):
for yy in range(y_min, y_max):
var c : Color = sprite.get_pixel(xx, yy)
if c == pixel_color:
if fill_with == Global.Fill_With.PATTERN && pattern_image: # Pattern fill
@ -428,27 +409,29 @@ func pencil_and_eraser(sprite : Image, mouse_pos : Vector2, color : Color, curre
func handle_undo(action : String) -> void:
if !can_undo:
return
var canvases := []
var frames := []
var frame_index := -1
var layer_index := -1
if Global.animation_timer.is_stopped(): # if we're not animating, store only the current canvas
canvases = [self]
frames.append(Global.frames[Global.current_frame])
frame_index = Global.current_frame
layer_index = Global.current_layer
else: # If we're animating, store all canvases
canvases = Global.canvases
else: # If we're animating, store all frames
frames = Global.frames
Global.undos += 1
Global.undo_redo.create_action(action)
for c in canvases:
for f in frames:
# I'm not sure why I have to unlock it, but...
# ...if I don't, it doesn't work properly
c.layers[Global.current_layer].image.unlock()
var data = c.layers[Global.current_layer].image.data
c.layers[Global.current_layer].image.lock()
Global.undo_redo.add_undo_property(c.layers[Global.current_layer].image, "data", data)
f.cels[Global.current_layer].image.unlock()
var data = f.cels[Global.current_layer].image.data
f.cels[Global.current_layer].image.lock()
Global.undo_redo.add_undo_property(f.cels[Global.current_layer].image, "data", data)
if action == "Rectangle Select":
var selected_pixels = Global.selected_pixels.duplicate()
Global.undo_redo.add_undo_property(Global.selection_rectangle, "polygon", Global.selection_rectangle.polygon)
Global.undo_redo.add_undo_property(Global, "selected_pixels", selected_pixels)
Global.undo_redo.add_undo_method(Global, "undo", canvases, layer_index)
Global.undo_redo.add_undo_method(Global, "undo", frame_index, layer_index)
can_undo = false
@ -458,28 +441,33 @@ func handle_redo(action : String) -> void:
if Global.undos < Global.undo_redo.get_version():
return
var canvases := []
var frames := []
var frame_index := -1
var layer_index := -1
if Global.animation_timer.is_stopped():
canvases = [self]
frames.append(Global.frames[Global.current_frame])
frame_index = Global.current_frame
layer_index = Global.current_layer
else:
canvases = Global.canvases
for c in canvases:
Global.undo_redo.add_do_property(c.layers[Global.current_layer].image, "data", c.layers[Global.current_layer].image.data)
frames = Global.frames
for f in frames:
Global.undo_redo.add_do_property(f.cels[Global.current_layer].image, "data", f.cels[Global.current_layer].image.data)
if action == "Rectangle Select":
Global.undo_redo.add_do_property(Global.selection_rectangle, "polygon", Global.selection_rectangle.polygon)
Global.undo_redo.add_do_property(Global, "selected_pixels", Global.selected_pixels)
Global.undo_redo.add_do_method(Global, "redo", canvases, layer_index)
Global.undo_redo.add_do_method(Global, "redo", frame_index, layer_index)
Global.undo_redo.commit_action()
func update_texture(layer_index : int) -> void:
layers[layer_index].image_texture.create_from_image(layers[layer_index].image, 0)
func update_texture(layer_index : int, frame_index := -1) -> void:
if frame_index == -1:
frame_index = Global.current_frame
var current_cel : Cel = Global.frames[frame_index].cels[layer_index]
current_cel.image_texture.create_from_image(current_cel.image, 0)
var frame_texture_rect : TextureRect
frame_texture_rect = Global.find_node_by_name(Global.layers[layer_index].frame_container.get_child(frame), "CelTexture")
frame_texture_rect.texture = layers[layer_index].image_texture
frame_texture_rect = Global.find_node_by_name(Global.layers[layer_index].frame_container.get_child(frame_index), "CelTexture")
frame_texture_rect.texture = current_cel.image_texture
func onion_skinning() -> void:
@ -493,7 +481,7 @@ func onion_skinning() -> void:
for i in range(1, Global.onion_skinning_past_rate + 1):
if Global.current_frame >= i:
var layer_i := 0
for layer in Global.canvases[Global.current_frame - i].layers:
for layer in Global.frames[Global.current_frame - i].cels:
if Global.layers[layer_i].visible:
color.a = 0.6 / i
draw_texture(layer.image_texture, location, color)
@ -507,9 +495,9 @@ func onion_skinning() -> void:
else:
color = Color.white
for i in range(1, Global.onion_skinning_future_rate + 1):
if Global.current_frame < Global.canvases.size() - i:
if Global.current_frame < Global.frames.size() - i:
var layer_i := 0
for layer in Global.canvases[Global.current_frame + i].layers:
for layer in Global.frames[Global.current_frame + i].cels:
if Global.layers[layer_i].visible:
color.a = 0.6 / i
draw_texture(layer.image_texture, location, color)

View file

@ -1,21 +1,11 @@
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
class_name Drawer
class SimpleDrawer:
func set_pixel(_sprite: Image, _pos: Vector2, _new_color: Color) -> void:
_sprite.set_pixel(_pos.x, _pos.y, _new_color)
class PixelPerfectDrawer extends Drawer:
class PixelPerfectDrawer:
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]
@ -38,3 +28,38 @@ class PixelPerfectDrawer extends Drawer:
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
var pixel_perfect := false setget set_pixel_perfect
var h_mirror := false
var v_mirror := false
var simple_drawer := SimpleDrawer.new()
var pixel_perfect_drawers = [PixelPerfectDrawer.new(), PixelPerfectDrawer.new(), PixelPerfectDrawer.new(), PixelPerfectDrawer.new()]
var drawers = [simple_drawer, simple_drawer, simple_drawer, simple_drawer]
func reset() -> void:
for drawer in pixel_perfect_drawers:
drawer.reset()
func set_pixel_perfect(value: bool) -> void:
pixel_perfect = value
if pixel_perfect:
drawers = pixel_perfect_drawers.duplicate()
else:
drawers = [simple_drawer, simple_drawer, simple_drawer, simple_drawer]
func set_pixel(_sprite: Image, _pos: Vector2, _new_color: Color) -> void:
var mirror_x = Global.canvas.x_max + Global.canvas.x_min - _pos.x - 1
var mirror_y = Global.canvas.y_max + Global.canvas.y_min - _pos.y - 1
drawers[0].set_pixel(_sprite, _pos, _new_color)
if h_mirror:
drawers[1].set_pixel(_sprite, Vector2(mirror_x, _pos.y), _new_color)
if v_mirror:
drawers[2].set_pixel(_sprite, Vector2(mirror_x, mirror_y), _new_color)
if v_mirror:
drawers[3].set_pixel(_sprite, Vector2(_pos.x, mirror_y), _new_color)

9
src/Classes/Frame.gd Normal file
View file

@ -0,0 +1,9 @@
class_name Frame extends Reference
# A class for frame properties.
# A frame is a collection of cels, for each layer.
var cels : Array # An array of Cels
func _init(_cels := []) -> void:
cels = _cels

View file

@ -364,10 +364,9 @@ func toggle_show_rulers() -> void:
func toggle_show_guides() -> void:
Global.show_guides = !Global.show_guides
view_menu.set_item_checked(3, Global.show_guides)
for canvas in Global.canvases:
for guide in canvas.get_children():
if guide is Guide:
guide.visible = Global.show_guides
for guide in Global.canvas.get_children():
if guide is Guide:
guide.visible = Global.show_guides
func toggle_show_anim_timeline() -> void:
@ -399,21 +398,21 @@ func show_scale_image_popup() -> void:
func crop_image() -> void:
# Use first cel as a starting rectangle
var used_rect : Rect2 = Global.canvases[0].layers[0].image.get_used_rect()
var used_rect : Rect2 = Global.frames[0].cels[0].image.get_used_rect()
for c in Global.canvases:
for f in Global.frames:
# However, if first cel is empty, loop through all cels until we find one that isn't
for layer in c.layers:
for cel in f.cels:
if used_rect != Rect2(0, 0, 0, 0):
break
else:
if layer.image.get_used_rect() != Rect2(0, 0, 0, 0):
used_rect = layer.image.get_used_rect()
if cel.image.get_used_rect() != Rect2(0, 0, 0, 0):
used_rect = cel.image.get_used_rect()
# Merge all layers with content
for layer in c.layers:
if layer.image.get_used_rect() != Rect2(0, 0, 0, 0):
used_rect = used_rect.merge(layer.image.get_used_rect())
for cel in f.cels:
if cel.image.get_used_rect() != Rect2(0, 0, 0, 0):
used_rect = used_rect.merge(cel.image.get_used_rect())
# If no layer has any content, just return
if used_rect == Rect2(0, 0, 0, 0):
@ -423,47 +422,41 @@ func crop_image() -> void:
var height := used_rect.size.y
Global.undos += 1
Global.undo_redo.create_action("Scale")
for c in Global.canvases:
Global.undo_redo.add_do_property(c, "size", Vector2(width, height).floor())
Global.undo_redo.add_do_property(Global.canvas, "size", Vector2(width, height).floor())
for f in Global.frames:
# Loop through all the layers to crop them
for j in range(Global.canvas.layers.size() - 1, -1, -1):
var sprite : Image = c.layers[j].image.get_rect(used_rect)
Global.undo_redo.add_do_property(c.layers[j].image, "data", sprite.data)
Global.undo_redo.add_undo_property(c.layers[j].image, "data", c.layers[j].image.data)
for j in range(Global.layers.size() - 1, -1, -1):
var sprite : Image = f.cels[j].image.get_rect(used_rect)
Global.undo_redo.add_do_property(f.cels[j].image, "data", sprite.data)
Global.undo_redo.add_undo_property(f.cels[j].image, "data", f.cels[j].image.data)
Global.undo_redo.add_undo_property(c, "size", c.size)
Global.undo_redo.add_undo_method(Global, "undo", Global.canvases)
Global.undo_redo.add_do_method(Global, "redo", Global.canvases)
Global.undo_redo.add_undo_property(Global.canvas, "size", Global.canvas.size)
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()
func flip_image_horizontal() -> void:
var canvas : Canvas = Global.canvas
canvas.handle_undo("Draw")
canvas.layers[Global.current_layer].image.unlock()
canvas.layers[Global.current_layer].image.flip_x()
canvas.layers[Global.current_layer].image.lock()
canvas.handle_redo("Draw")
func flip_image_vertical() -> void:
var canvas : Canvas = Global.canvas
canvas.handle_undo("Draw")
canvas.layers[Global.current_layer].image.unlock()
canvas.layers[Global.current_layer].image.flip_y()
canvas.layers[Global.current_layer].image.lock()
canvas.handle_redo("Draw")
func flip_image(horizontal : bool) -> void:
var image : Image = Global.frames[Global.current_frame].cels[Global.current_layer].image
Global.canvas.handle_undo("Draw")
image.unlock()
if horizontal:
image.flip_x()
else:
image.flip_y()
image.lock()
Global.canvas.handle_redo("Draw")
func show_rotate_image_popup() -> void:
var image : Image = Global.canvas.layers[Global.current_layer].image
var image : Image = Global.frames[Global.current_frame].cels[Global.current_layer].image
$RotateImage.set_sprite(image)
$RotateImage.popup_centered()
Global.dialog_open(true)
func invert_image_colors() -> void:
var image : Image = Global.canvas.layers[Global.current_layer].image
var image : Image = Global.frames[Global.current_frame].cels[Global.current_layer].image
Global.canvas.handle_undo("Draw")
for xx in image.get_size().x:
for yy in image.get_size().y:
@ -475,7 +468,7 @@ func invert_image_colors() -> void:
func desaturate_image() -> void:
var image : Image = Global.canvas.layers[Global.current_layer].image
var image : Image = Global.frames[Global.current_frame].cels[Global.current_layer].image
Global.canvas.handle_undo("Draw")
for xx in image.get_size().x:
for yy in image.get_size().y:
@ -509,10 +502,10 @@ func image_menu_id_pressed(id : int) -> void:
crop_image()
2: # Flip Horizontal
flip_image_horizontal()
flip_image(true)
3: # Flip Vertical
flip_image_vertical()
flip_image(false)
4: # Rotate
show_rotate_image_popup()

View file

@ -37,11 +37,6 @@ func _ready() -> void:
for pref in preferences:
var node = right_side.get_node(pref[1])
if Global.config_cache.has_section_key("preferences", pref[0]):
var value = Global.config_cache.get_value("preferences", pref[0])
Global.set(pref[0], value)
node.set(pref[2], value)
match pref[2]:
"pressed":
node.connect("toggled", self, "_on_Preference_toggled", [pref[0]])
@ -53,12 +48,10 @@ func _ready() -> void:
"selected":
node.connect("item_selected", self, "_on_Preference_item_selected", [pref[0]])
Global.transparent_checker._ready()
for canvas in Global.canvases:
for guide in canvas.get_children():
if guide is Guide:
guide.default_color = Global.guide_color
if Global.config_cache.has_section_key("preferences", pref[0]):
var value = Global.config_cache.get_value("preferences", pref[0])
Global.set(pref[0], value)
node.set(pref[2], value)
func _on_Preference_toggled(button_pressed : bool, prop : String) -> void:
@ -96,10 +89,9 @@ func preference_update(prop : String) -> void:
Global.transparent_checker._ready()
if prop in ["guide_color"]:
for canvas in Global.canvases:
for guide in canvas.get_children():
if guide is Guide:
guide.default_color = Global.guide_color
for guide in Global.canvas.get_children():
if guide is Guide:
guide.default_color = Global.guide_color
Global.config_cache.save("user://cache.ini")

View file

@ -27,7 +27,7 @@ func _process(_delta : float) -> void:
var start_pos := polygon[0]
var end_pos := polygon[2]
var current_layer_index : int = Global.current_layer
var layer : Image = Global.canvas.layers[current_layer_index].image
var layer : Image = Global.frames[Global.current_frame].cels[current_layer_index].image
if end_pos == start_pos:
visible = false

View file

@ -73,22 +73,23 @@ func _on_CreateNewImage_confirmed() -> void:
var width : int = width_value.value
var height : int = height_value.value
var fill_color : Color = fill_color_node.color
Global.clear_canvases()
Global.clear_frames()
Global.layers.clear()
Global.layers.append(Layer.new())
Global.canvas = load("res://src/Canvas.tscn").instance()
Global.canvas.size = Vector2(width, height).floor()
Global.canvas.fill_color = fill_color
var frame : Frame = Global.canvas.new_empty_frame()
Global.canvas.camera_zoom()
Global.frames.append(frame)
Global.canvases.append(Global.canvas)
Global.canvas_parent.add_child(Global.canvas)
Global.current_layer = 0
Global.canvases = Global.canvases # To trigger Global.canvases_changed()
Global.frames = Global.frames # To trigger Global.frames_changed()
Global.current_frame = 0
Global.layers = Global.layers # To trigger Global.layers_changed()
Global.project_has_changed = false
if fill_color.a > 0:
Global.canvas.layers[0].image.fill(fill_color)
Global.canvas.layers[0].image.lock()
Global.frames[0].cels[0].image.fill(fill_color)
Global.frames[0].cels[0].image.lock()
Global.canvas.update_texture(0)

View file

@ -3,7 +3,7 @@ extends AcceptDialog
enum ExportTab { FRAME = 0, SPRITESHEET = 1, ANIMATION = 2 }
var current_tab : int = ExportTab.FRAME
# All canvases and their layers processed/blended into images
# All frames and their layers processed/blended into images
var processed_images = [] # Image[]
# Frame options
@ -11,7 +11,7 @@ var frame_number := 0
# Spritesheet options
var frame_current_tag := 0 # Export only current frame tag
var canvas_size := 1
var number_of_frames := 1
enum Orientation { ROWS = 0, COLUMNS = 1 }
var orientation : int = Orientation.ROWS
# How many rows/columns before new line is added
@ -90,7 +90,7 @@ func show_tab() -> void:
$FrameTimer.stop()
if not was_exported:
frame_number = Global.current_frame + 1
$VBoxContainer/FrameOptions/FrameNumber/FrameNumber.max_value = Global.canvases.size() + 1
$VBoxContainer/FrameOptions/FrameNumber/FrameNumber.max_value = Global.frames.size() + 1
var prev_frame_number = $VBoxContainer/FrameOptions/FrameNumber/FrameNumber.value
$VBoxContainer/FrameOptions/FrameNumber/FrameNumber.value = frame_number
if prev_frame_number == frame_number:
@ -101,13 +101,13 @@ func show_tab() -> void:
file_format = FileFormat.PNG
if not was_exported:
orientation = Orientation.ROWS
lines_count = int(ceil(sqrt(canvas_size)))
lines_count = int(ceil(sqrt(number_of_frames)))
process_spritesheet()
$VBoxContainer/File/FileFormat.selected = FileFormat.PNG
$VBoxContainer/SpritesheetOptions/Frames/Frames.select(frame_current_tag)
$FrameTimer.stop()
$VBoxContainer/SpritesheetOptions/Orientation/Orientation.selected = orientation
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.max_value = canvas_size
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.max_value = number_of_frames
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.value = lines_count
$VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:"
$VBoxContainer/SpritesheetOptions.show()
@ -135,10 +135,10 @@ func external_export() -> void:
func process_frame() -> void:
var canvas = Global.canvases[frame_number - 1]
var frame = Global.frames[frame_number - 1]
var image := Image.new()
image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
blend_layers(image, canvas)
image.create(Global.canvas.size.x, Global.canvas.size.y, false, Image.FORMAT_RGBA8)
blend_layers(image, frame)
processed_images.clear()
processed_images.append(image)
@ -149,12 +149,12 @@ func process_spritesheet() -> void:
if frame_current_tag > 0:
var frame_start = Global.animation_tags[frame_current_tag - 1].from
var frame_end = Global.animation_tags[frame_current_tag - 1].to
frames = Global.canvases.slice(frame_start-1, frame_end-1, 1, true)
frames = Global.frames.slice(frame_start-1, frame_end-1, 1, true)
else:
frames = Global.canvases
frames = Global.frames
# Then store the size of frames for other functions
canvas_size = frames.size()
number_of_frames = frames.size()
# If rows mode selected calculate columns count and vice versa
var spritesheet_columns = lines_count if orientation == Orientation.ROWS else frames_divided_by_spritesheet_lines()
@ -170,26 +170,26 @@ func process_spritesheet() -> void:
var hh := 0
var vv := 0
for canvas in frames:
for frame in frames:
if orientation == Orientation.ROWS:
if vv < spritesheet_columns:
origin.x = canvas.size.x * vv
origin.x = Global.canvas.size.x * vv
vv += 1
else:
hh += 1
origin.x = 0
vv = 1
origin.y = canvas.size.y * hh
origin.y = Global.canvas.size.y * hh
else:
if hh < spritesheet_rows:
origin.y = canvas.size.y * hh
origin.y = Global.canvas.size.y * hh
hh += 1
else:
vv += 1
origin.y = 0
hh = 1
origin.x = canvas.size.x * vv
blend_layers(whole_image, canvas, origin)
origin.x = Global.canvas.size.x * vv
blend_layers(whole_image, frame, origin)
processed_images.clear()
processed_images.append(whole_image)
@ -197,10 +197,10 @@ func process_spritesheet() -> void:
func process_animation() -> void:
processed_images.clear()
for canvas in Global.canvases:
for frame in Global.frames:
var image := Image.new()
image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
blend_layers(image, canvas)
image.create(Global.canvas.size.x, Global.canvas.size.y, false, Image.FORMAT_RGBA8)
blend_layers(image, frame)
processed_images.append(image)
@ -361,21 +361,21 @@ func export_processed_images(ignore_overwrites : bool) -> void:
# Blends canvas layers into passed image starting from the origin position
func blend_layers(image: Image, canvas: Canvas, origin: Vector2 = Vector2(0, 0)) -> void:
func blend_layers(image : Image, frame : Frame, origin : Vector2 = Vector2(0, 0)) -> void:
image.lock()
var layer_i := 0
for layer in canvas.layers:
for cel in frame.cels:
if Global.layers[layer_i].visible:
var layer_image := Image.new()
layer_image.copy_from(layer.image)
layer_image.lock()
if layer.opacity < 1: # If we have layer transparency
for xx in layer_image.get_size().x:
for yy in layer_image.get_size().y:
var pixel_color := layer_image.get_pixel(xx, yy)
var alpha : float = pixel_color.a * layer.opacity
layer_image.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha))
DrawingAlgos.blend_rect(image, layer_image, Rect2(canvas.position, canvas.size), origin)
var cel_image := Image.new()
cel_image.copy_from(cel.image)
cel_image.lock()
if cel.opacity < 1: # If we have cel transparency
for xx in cel_image.get_size().x:
for yy in cel_image.get_size().y:
var pixel_color := cel_image.get_pixel(xx, yy)
var alpha : float = pixel_color.a * cel.opacity
cel_image.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha))
DrawingAlgos.blend_rect(image, cel_image, Rect2(Global.canvas.location, Global.canvas.size), origin)
layer_i += 1
image.unlock()
@ -416,7 +416,7 @@ func create_export_path(multifile: bool, frame: int = 0) -> String:
func frames_divided_by_spritesheet_lines() -> int:
return int(ceil(canvas_size / float(lines_count)))
return int(ceil(number_of_frames / float(lines_count)))
func file_format_string(format_enum : int) -> String:
@ -476,7 +476,7 @@ func store_export_settings() -> void:
# Fill the dialog with previous export settings
func restore_previous_export_settings() -> void:
current_tab = exported_tab
frame_number = exported_frame_number if exported_frame_number <= Global.canvases.size() else Global.canvases.size()
frame_number = exported_frame_number if exported_frame_number <= Global.frames.size() else Global.frames.size()
frame_current_tag = exported_frame_current_tag if exported_frame_current_tag <= Global.animation_tags.size() else 0
orientation = exported_orientation
lines_count = exported_lines_count
@ -667,5 +667,5 @@ func _on_Frames_item_selected(id : int) -> void:
frame_current_tag = id
process_spritesheet()
set_preview()
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.max_value = canvas_size
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.max_value = number_of_frames
$VBoxContainer/SpritesheetOptions/Orientation/LinesCount.value = lines_count

View file

@ -1,6 +1,6 @@
extends WindowDialog
var current_layer : Image
var current_cel : Image
var preview_image : Image
var preview_texture : ImageTexture
@ -16,15 +16,15 @@ onready var preview = $MarginContainer/VBoxContainer/TextureRect
func _ready() -> void:
current_layer = Image.new()
current_cel = Image.new()
preview_image = Image.new()
preview_texture = ImageTexture.new()
preview_texture.flags = 0
func _on_HSVDialog_about_to_show() -> void:
current_layer = Global.canvas.layers[Global.current_layer].image
preview_image.copy_from(current_layer)
current_cel = Global.frames[Global.current_frame].cels[Global.current_layer].image
preview_image.copy_from(current_cel)
update_preview()
@ -35,9 +35,9 @@ func _on_Cancel_pressed() -> void:
func _on_Apply_pressed() -> void:
Global.canvas.handle_undo("Draw")
DrawingAlgos.adjust_hsv(current_layer,0,hue_slider.value)
DrawingAlgos.adjust_hsv(current_layer,1,sat_slider.value)
DrawingAlgos.adjust_hsv(current_layer,2,val_slider.value)
DrawingAlgos.adjust_hsv(current_cel,0,hue_slider.value)
DrawingAlgos.adjust_hsv(current_cel,1,sat_slider.value)
DrawingAlgos.adjust_hsv(current_cel,2,val_slider.value)
Global.canvas.update_texture(Global.current_layer)
Global.canvas.handle_redo("Draw")
reset()
@ -56,7 +56,7 @@ func reset() -> void:
func update_preview() -> void:
preview_image.copy_from(current_layer)
preview_image.copy_from(current_cel)
DrawingAlgos.adjust_hsv(preview_image,0,hue_slider.value)
DrawingAlgos.adjust_hsv(preview_image,1,sat_slider.value)
DrawingAlgos.adjust_hsv(preview_image,2,val_slider.value)

View file

@ -37,19 +37,14 @@ func _on_VerticalFrames_value_changed(value) -> void:
func _on_ImportSprites_files_selected(paths : PoolStringArray) -> void:
Global.control.opensprite_file_selected = true
if !new_frame: # If we're not adding a new frame, delete the previous
Global.clear_canvases()
Global.clear_frames()
Global.layers.clear()
# Store [Layer name (0), Layer visibility boolean (1), Layer lock boolean (2), Frame container (3),
# will new frames be linked boolean (4), Array of linked frames (5)]
Global.layers.append(Layer.new())
Global.current_layer = 0
var first_path : String = paths[0]
var i : int = Global.canvases.size()
var i : int = Global.frames.size()
if !import_spritesheet:
# Find the biggest image and let it handle the camera zoom options
var max_size : Vector2
var biggest_canvas : Canvas
for path in paths:
var image := Image.new()
var err := image.load(path)
@ -60,35 +55,23 @@ func _on_ImportSprites_files_selected(paths : PoolStringArray) -> void:
Global.dialog_open(true)
continue
var canvas : Canvas = load("res://src/Canvas.tscn").instance()
canvas.size = image.get_size()
Global.canvas.size = image.get_size()
var frame := Frame.new()
image.convert(Image.FORMAT_RGBA8)
image.lock()
canvas.layers.append(Cel.new(image, 1))
frame.cels.append(Cel.new(image, 1))
for _i in range(1, Global.layers.size()):
var empty_sprite := Image.new()
empty_sprite.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
empty_sprite.create(Global.canvas.size.x, Global.canvas.size.y, false, Image.FORMAT_RGBA8)
empty_sprite.fill(Color(0, 0, 0, 0))
empty_sprite.lock()
canvas.layers.append(Cel.new(empty_sprite, 1))
frame.cels.append(Cel.new(empty_sprite, 1))
canvas.frame = i
Global.canvases.append(canvas)
Global.canvas_parent.add_child(canvas)
canvas.visible = false
if path == paths[0]: # If it's the first file
max_size = canvas.size
biggest_canvas = canvas
else:
if canvas.size > max_size:
biggest_canvas = canvas
Global.frames.append(frame)
i += 1
if biggest_canvas:
biggest_canvas.camera_zoom()
else:
var image := Image.new()
var err := image.load(first_path)
@ -105,36 +88,31 @@ func _on_ImportSprites_files_selected(paths : PoolStringArray) -> void:
var frame_height := image.get_size().y / spritesheet_vertical
for yy in range(spritesheet_vertical):
for xx in range(spritesheet_horizontal):
var canvas : Canvas = load("res://src/Canvas.tscn").instance()
var frame := Frame.new()
var cropped_image := Image.new()
cropped_image = image.get_rect(Rect2(frame_width * xx, frame_height * yy, frame_width, frame_height))
canvas.size = cropped_image.get_size()
Global.canvas.size = cropped_image.get_size()
cropped_image.convert(Image.FORMAT_RGBA8)
cropped_image.lock()
canvas.layers.append(Cel.new(cropped_image, 1))
frame.cels.append(Cel.new(cropped_image, 1))
for _i in range(1, Global.layers.size()):
var empty_sprite := Image.new()
empty_sprite.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8)
empty_sprite.create(Global.canvas.size.x, Global.canvas.size.y, false, Image.FORMAT_RGBA8)
empty_sprite.fill(Color(0, 0, 0, 0))
empty_sprite.lock()
canvas.layers.append(Cel.new(empty_sprite, 1))
frame.cels.append(Cel.new(empty_sprite, 1))
canvas.frame = i
Global.canvases.append(canvas)
Global.canvas_parent.add_child(canvas)
canvas.visible = false
Global.frames.append(frame)
i += 1
Global.canvases[Global.canvases.size() - 1].camera_zoom()
Global.canvas.camera_zoom()
Global.canvases = Global.canvases # Just to call Global.canvases_changed
Global.frames = Global.frames # Just to call Global.frames_changed
Global.current_frame = i - 1
Global.canvas = Global.canvases[Global.canvases.size() - 1]
if !new_frame:
Global.layers = Global.layers # Just to call Global.layers_changed
Global.canvas.visible = true
Global.window_title = first_path.get_file() + " (" + tr("imported") + ") - Pixelorama " + Global.current_version
if Global.project_has_changed:
@ -143,4 +121,3 @@ func _on_ImportSprites_files_selected(paths : PoolStringArray) -> void:
var directory_path := first_path.get_basename().replace(file_name, "")
Global.export_dialog.directory_path = directory_path
Global.export_dialog.file_name = file_name

View file

@ -10,7 +10,7 @@ func _on_OutlineDialog_confirmed() -> void:
var diagonal : bool = $OptionsContainer/DiagonalCheckBox.pressed
var inside_image : bool = $OptionsContainer/InsideImageCheckBox.pressed
var image : Image = Global.canvas.layers[Global.current_layer].image
var image : Image = Global.frames[Global.current_frame].cels[Global.current_layer].image
if image.is_invisible():
return
var new_image := Image.new()

View file

@ -7,17 +7,17 @@ func _on_ScaleImage_confirmed() -> void:
var interpolation : int = $VBoxContainer/OptionsContainer/InterpolationType.selected
Global.undos += 1
Global.undo_redo.create_action("Scale")
Global.undo_redo.add_do_property(Global.canvas, "size", Vector2(width, height).floor())
for c in Global.canvases:
Global.undo_redo.add_do_property(c, "size", Vector2(width, height).floor())
for i in range(c.layers.size() - 1, -1, -1):
for f in Global.frames:
for i in range(f.cels.size() - 1, -1, -1):
var sprite := Image.new()
sprite.copy_from(c.layers[i].image)
sprite.copy_from(f.cels[i].image)
sprite.resize(width, height, interpolation)
Global.undo_redo.add_do_property(c.layers[i].image, "data", sprite.data)
Global.undo_redo.add_undo_property(c.layers[i].image, "data", c.layers[i].image.data)
Global.undo_redo.add_undo_property(c, "size", c.size)
Global.undo_redo.add_do_property(f.cels[i].image, "data", sprite.data)
Global.undo_redo.add_undo_property(f.cels[i].image, "data", f.cels[i].image.data)
Global.undo_redo.add_undo_method(Global, "undo", Global.canvases)
Global.undo_redo.add_do_method(Global, "redo", Global.canvases)
Global.undo_redo.add_undo_property(Global.canvas, "size", Global.canvas.size)
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()

View file

@ -4,7 +4,7 @@ var fps := 6.0
var animation_loop := 1 # 0 is no loop, 1 is cycle loop, 2 is ping-pong loop
var animation_forward := true
var first_frame := 0
var last_frame : int = Global.canvases.size() - 1
var last_frame : int = Global.frames.size() - 1
var timeline_scroll : ScrollContainer
var tag_scroll_container : ScrollContainer
@ -24,47 +24,48 @@ func _h_scroll_changed(value : float) -> void:
func add_frame() -> void:
var new_canvas : Canvas = load("res://src/Canvas.tscn").instance()
new_canvas.size = Global.canvas.size
new_canvas.frame = Global.canvases.size()
var frame : Frame = Global.canvas.new_empty_frame()
var new_frames : Array = Global.frames.duplicate()
new_frames.append(frame)
var new_layers : Array = Global.layers.duplicate()
# Loop through the array to create new classes for each element, so that they
# won't be the same as the original array's classes. Needed for undo/redo to work properly.
for i in new_layers.size():
var new_linked_cels = new_layers[i].linked_cels.duplicate()
new_layers[i] = Layer.new(new_layers[i].name, new_layers[i].visible, new_layers[i].locked, new_layers[i].frame_container, new_layers[i].new_cels_linked, new_linked_cels)
var new_canvases : Array = Global.canvases.duplicate()
new_canvases.append(new_canvas)
for l_i in range(new_layers.size()):
if new_layers[l_i].new_cels_linked: # If the link button is pressed
new_layers[l_i].linked_cels.append(frame)
frame.cels[l_i].image = new_layers[l_i].linked_cels[0].cels[l_i].image
frame.cels[l_i].image_texture = new_layers[l_i].linked_cels[0].cels[l_i].image_texture
Global.undos += 1
Global.undo_redo.create_action("Add Frame")
Global.undo_redo.add_do_method(Global, "redo", [new_canvas])
Global.undo_redo.add_undo_method(Global, "undo", [new_canvas])
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
Global.undo_redo.add_do_property(Global, "canvas", new_canvas)
Global.undo_redo.add_do_property(Global, "current_frame", new_canvases.size() - 1)
Global.undo_redo.add_do_property(Global, "frames", new_frames)
Global.undo_redo.add_do_property(Global, "current_frame", new_frames.size() - 1)
Global.undo_redo.add_do_property(Global, "layers", new_layers)
for c in Global.canvases:
Global.undo_redo.add_do_property(c, "visible", false)
Global.undo_redo.add_undo_property(c, "visible", c.visible)
for l_i in range(Global.layers.size()):
if Global.layers[l_i].new_cels_linked: # If the link button is pressed
Global.layers[l_i].linked_cels.append(new_canvas)
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
Global.undo_redo.add_undo_property(Global, "canvas", Global.canvas)
Global.undo_redo.add_undo_property(Global, "frames", Global.frames)
Global.undo_redo.add_undo_property(Global, "current_frame", Global.current_frame)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.commit_action()
func _on_DeleteFrame_pressed(frame := -1) -> void:
if Global.canvases.size() == 1:
if Global.frames.size() == 1:
return
if frame == -1:
frame = Global.current_frame
var canvas : Canvas = Global.canvases[frame]
var new_canvases : Array = Global.canvases.duplicate()
new_canvases.erase(canvas)
var frame_to_delete : Frame = Global.frames[frame]
var new_frames : Array = Global.frames.duplicate()
new_frames.erase(frame_to_delete)
var current_frame := Global.current_frame
if current_frame > 0 && current_frame == new_canvases.size(): # If it's the last frame
if current_frame > 0 && current_frame == new_frames.size(): # If it's the last frame
current_frame -= 1
var new_animation_tags := Global.animation_tags.duplicate()
@ -96,34 +97,24 @@ func _on_DeleteFrame_pressed(frame := -1) -> void:
for layer in new_layers:
for linked in layer.linked_cels:
if linked == Global.canvases[frame]:
if linked == Global.frames[frame]:
layer.linked_cels.erase(linked)
Global.undos += 1
Global.undo_redo.create_action("Remove Frame")
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
Global.undo_redo.add_do_property(Global, "canvas", new_canvases[current_frame])
Global.undo_redo.add_do_property(Global, "frames", new_frames)
Global.undo_redo.add_do_property(Global, "current_frame", current_frame)
Global.undo_redo.add_do_property(Global, "animation_tags", new_animation_tags)
Global.undo_redo.add_do_property(Global, "layers", new_layers)
# Change the frame value of the canvaseso on the right
# for example, if frame "3" was deleted, then "4" would have to become "3"
for i in range(frame, new_canvases.size()):
var c : Canvas = new_canvases[i]
Global.undo_redo.add_do_property(c, "frame", i)
Global.undo_redo.add_undo_property(c, "frame", c.frame)
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
Global.undo_redo.add_undo_property(Global, "canvas", canvas)
Global.undo_redo.add_undo_property(Global, "frames", Global.frames)
Global.undo_redo.add_undo_property(Global, "current_frame", Global.current_frame)
Global.undo_redo.add_undo_property(Global, "animation_tags", Global.animation_tags)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_do_method(Global, "redo", [canvas])
Global.undo_redo.add_undo_method(Global, "undo", [canvas])
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.commit_action()
@ -131,19 +122,16 @@ func _on_CopyFrame_pressed(frame := -1) -> void:
if frame == -1:
frame = Global.current_frame
var canvas : Canvas = Global.canvases[frame]
var new_canvas : Canvas = load("res://src/Canvas.tscn").instance()
new_canvas.size = Global.canvas.size
new_canvas.frame = Global.canvases.size()
var new_frame := Frame.new()
var new_canvases := Global.canvases.duplicate()
new_canvases.insert(frame + 1, new_canvas)
var new_frames := Global.frames.duplicate()
new_frames.insert(frame + 1, new_frame)
for layer in canvas.layers: # Copy every layer
for cel in Global.frames[frame].cels: # Copy every cel
var sprite := Image.new()
sprite.copy_from(layer.image)
sprite.copy_from(cel.image)
sprite.lock()
new_canvas.layers.append(Cel.new(sprite, layer.opacity))
new_frame.cels.append(Cel.new(sprite, cel.opacity))
var new_animation_tags := Global.animation_tags.duplicate()
# Loop through the tags to create new classes for them, so that they won't be the same
@ -158,28 +146,18 @@ func _on_CopyFrame_pressed(frame := -1) -> void:
Global.undos += 1
Global.undo_redo.create_action("Add Frame")
Global.undo_redo.add_do_method(Global, "redo", [new_canvas])
Global.undo_redo.add_undo_method(Global, "undo", [new_canvas])
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
Global.undo_redo.add_do_property(Global, "canvas", new_canvas)
Global.undo_redo.add_do_property(Global, "frames", new_frames)
Global.undo_redo.add_do_property(Global, "current_frame", frame + 1)
Global.undo_redo.add_do_property(Global, "animation_tags", new_animation_tags)
for i in range(Global.layers.size()):
for child in Global.layers[i].frame_container.get_children():
Global.undo_redo.add_do_property(child, "pressed", false)
Global.undo_redo.add_undo_property(child, "pressed", child.pressed)
for c in Global.canvases:
Global.undo_redo.add_do_property(c, "visible", false)
Global.undo_redo.add_undo_property(c, "visible", c.visible)
for i in range(frame, new_canvases.size()):
var c : Canvas = new_canvases[i]
Global.undo_redo.add_do_property(c, "frame", i)
Global.undo_redo.add_undo_property(c, "frame", c.frame)
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
Global.undo_redo.add_undo_property(Global, "canvas", Global.canvas)
Global.undo_redo.add_undo_property(Global, "frames", Global.frames)
Global.undo_redo.add_undo_property(Global, "current_frame", frame)
Global.undo_redo.add_undo_property(Global, "animation_tags", Global.animation_tags)
Global.undo_redo.commit_action()
@ -276,12 +254,12 @@ func _on_AnimationTimer_timeout() -> void:
func play_animation(play : bool, forward_dir : bool) -> void:
first_frame = 0
last_frame = Global.canvases.size() - 1
last_frame = Global.frames.size() - 1
if Global.play_only_tags:
for tag in Global.animation_tags:
if Global.current_frame + 1 >= tag.from && Global.current_frame + 1 <= tag.to:
first_frame = tag.from - 1
last_frame = min(Global.canvases.size() - 1, tag.to - 1)
last_frame = min(Global.frames.size() - 1, tag.to - 1)
if first_frame == last_frame:
if forward_dir:
@ -310,7 +288,7 @@ func play_animation(play : bool, forward_dir : bool) -> void:
func _on_NextFrame_pressed() -> void:
if Global.current_frame < Global.canvases.size() - 1:
if Global.current_frame < Global.frames.size() - 1:
Global.current_frame += 1
@ -320,7 +298,7 @@ func _on_PreviousFrame_pressed() -> void:
func _on_LastFrame_pressed() -> void:
Global.current_frame = Global.canvases.size() - 1
Global.current_frame = Global.frames.size() - 1
func _on_FirstFrame_pressed() -> void:
@ -359,27 +337,27 @@ func add_layer(is_new := true) -> void:
Global.undos += 1
Global.undo_redo.create_action("Add Layer")
for c in Global.canvases:
for f in Global.frames:
var new_layer := Image.new()
if is_new:
new_layer.create(c.size.x, c.size.y, false, Image.FORMAT_RGBA8)
new_layer.create(Global.canvas.size.x, Global.canvas.size.y, false, Image.FORMAT_RGBA8)
else: # Clone layer
new_layer.copy_from(c.layers[Global.current_layer].image)
new_layer.copy_from(f.cels[Global.current_layer].image)
new_layer.lock()
var new_canvas_layers : Array = c.layers.duplicate()
new_canvas_layers.append(Cel.new(new_layer, 1))
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
Global.undo_redo.add_undo_property(c, "layers", c.layers)
var new_cels : Array = f.cels.duplicate()
new_cels.append(Cel.new(new_layer, 1))
Global.undo_redo.add_do_property(f, "cels", new_cels)
Global.undo_redo.add_undo_property(f, "cels", f.cels)
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer + 1)
Global.undo_redo.add_do_property(Global, "layers", new_layers)
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()
@ -393,17 +371,17 @@ func _on_RemoveLayer_pressed() -> void:
else:
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer)
for c in Global.canvases:
var new_canvas_layers : Array = c.layers.duplicate()
new_canvas_layers.remove(Global.current_layer)
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
Global.undo_redo.add_undo_property(c, "layers", c.layers)
for f in Global.frames:
var new_cels : Array = f.cels.duplicate()
new_cels.remove(Global.current_layer)
Global.undo_redo.add_do_property(f, "cels", new_cels)
Global.undo_redo.add_undo_property(f, "cels", f.cels)
Global.undo_redo.add_do_property(Global, "layers", new_layers)
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.commit_action()
@ -415,21 +393,21 @@ func change_layer_order(rate : int) -> void:
new_layers[Global.current_layer] = new_layers[change]
new_layers[change] = temp
Global.undo_redo.create_action("Change Layer Order")
for c in Global.canvases:
var new_canvas_layers : Array = c.layers.duplicate()
var temp_canvas = new_canvas_layers[Global.current_layer]
new_canvas_layers[Global.current_layer] = new_canvas_layers[change]
new_canvas_layers[change] = temp_canvas
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
Global.undo_redo.add_undo_property(c, "layers", c.layers)
for f in Global.frames:
var new_cels : Array = f.cels.duplicate()
var temp_canvas = new_cels[Global.current_layer]
new_cels[Global.current_layer] = new_cels[change]
new_cels[change] = temp_canvas
Global.undo_redo.add_do_property(f, "cels", new_cels)
Global.undo_redo.add_undo_property(f, "cels", f.cels)
Global.undo_redo.add_do_property(Global, "current_layer", change)
Global.undo_redo.add_do_property(Global, "layers", new_layers)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvas])
Global.undo_redo.add_do_method(Global, "redo", [Global.canvas])
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()
@ -443,35 +421,35 @@ func _on_MergeDownLayer_pressed() -> void:
Global.undos += 1
Global.undo_redo.create_action("Merge Layer")
for c in Global.canvases:
var new_canvas_layers : Array = c.layers.duplicate()
for i in new_canvas_layers.size():
new_canvas_layers[i] = Cel.new(new_canvas_layers[i].image, new_canvas_layers[i].opacity)
for f in Global.frames:
var new_cels : Array = f.cels.duplicate()
for i in new_cels.size():
new_cels[i] = Cel.new(new_cels[i].image, new_cels[i].opacity)
var selected_layer := Image.new()
selected_layer.copy_from(new_canvas_layers[Global.current_layer].image)
selected_layer.copy_from(new_cels[Global.current_layer].image)
selected_layer.lock()
if c.layers[Global.current_layer].opacity < 1: # If we have layer transparency
if f.cels[Global.current_layer].opacity < 1: # If we have layer transparency
for xx in selected_layer.get_size().x:
for yy in selected_layer.get_size().y:
var pixel_color : Color = selected_layer.get_pixel(xx, yy)
var alpha : float = pixel_color.a * c.layers[Global.current_layer].opacity
var alpha : float = pixel_color.a * f.cels[Global.current_layer].opacity
selected_layer.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha))
var new_layer := Image.new()
new_layer.copy_from(c.layers[Global.current_layer - 1].image)
new_layer.copy_from(f.cels[Global.current_layer - 1].image)
new_layer.lock()
DrawingAlgos.blend_rect(new_layer, selected_layer, Rect2(c.position, c.size), Vector2.ZERO)
new_canvas_layers.remove(Global.current_layer)
if !selected_layer.is_invisible() and Global.layers[Global.current_layer - 1].linked_cels.size() > 1 and (c in Global.layers[Global.current_layer - 1].linked_cels):
new_layers[Global.current_layer - 1].linked_cels.erase(c)
new_canvas_layers[Global.current_layer - 1].image = new_layer
DrawingAlgos.blend_rect(new_layer, selected_layer, Rect2(Global.canvas.location, Global.canvas.size), Vector2.ZERO)
new_cels.remove(Global.current_layer)
if !selected_layer.is_invisible() and Global.layers[Global.current_layer - 1].linked_cels.size() > 1 and (f in Global.layers[Global.current_layer - 1].linked_cels):
new_layers[Global.current_layer - 1].linked_cels.erase(f)
new_cels[Global.current_layer - 1].image = new_layer
else:
Global.undo_redo.add_do_property(c.layers[Global.current_layer - 1].image, "data", new_layer.data)
Global.undo_redo.add_undo_property(c.layers[Global.current_layer - 1].image, "data", c.layers[Global.current_layer - 1].image.data)
Global.undo_redo.add_do_property(f.cels[Global.current_layer - 1].image, "data", new_layer.data)
Global.undo_redo.add_undo_property(f.cels[Global.current_layer - 1].image, "data", f.cels[Global.current_layer - 1].image.data)
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
Global.undo_redo.add_undo_property(c, "layers", c.layers)
Global.undo_redo.add_do_property(f, "cels", new_cels)
Global.undo_redo.add_undo_property(f, "cels", f.cels)
new_layers.remove(Global.current_layer)
Global.undo_redo.add_do_property(Global, "current_layer", Global.current_layer - 1)
@ -479,13 +457,13 @@ func _on_MergeDownLayer_pressed() -> void:
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_undo_property(Global, "current_layer", Global.current_layer)
Global.undo_redo.add_undo_method(Global, "undo", Global.canvases)
Global.undo_redo.add_do_method(Global, "redo", Global.canvases)
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()
func _on_OpacitySlider_value_changed(value) -> void:
Global.canvas.layers[Global.current_layer].opacity = value / 100
Global.frames[Global.current_frame].cels[Global.current_layer].opacity = value / 100
Global.layer_opacity_slider.value = value
Global.layer_opacity_slider.value = value
Global.layer_opacity_spinbox.value = value

View file

@ -8,7 +8,7 @@ onready var popup_menu : PopupMenu = $PopupMenu
func _ready() -> void:
hint_tooltip = "Frame: %s, Layer: %s" % [frame + 1, layer]
if Global.canvases[frame] in Global.layers[layer].linked_cels:
if Global.frames[frame] in Global.layers[layer].linked_cels:
get_node("LinkedIndicator").visible = true
popup_menu.set_item_text(4, "Unlink Cel")
popup_menu.set_item_metadata(4, "Unlink Cel")
@ -23,7 +23,7 @@ func _on_CelButton_pressed() -> void:
Global.current_frame = frame
Global.current_layer = layer
elif Input.is_action_just_released("right_mouse"):
if Global.canvases.size() == 1:
if Global.frames.size() == 1:
popup_menu.set_item_disabled(0, true)
popup_menu.set_item_disabled(2, true)
popup_menu.set_item_disabled(3, true)
@ -31,7 +31,7 @@ func _on_CelButton_pressed() -> void:
popup_menu.set_item_disabled(0, false)
if frame > 0:
popup_menu.set_item_disabled(2, false)
if frame < Global.canvases.size() - 1:
if frame < Global.frames.size() - 1:
popup_menu.set_item_disabled(3, false)
popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE))
pressed = !pressed
@ -53,73 +53,68 @@ func _on_PopupMenu_id_pressed(ID : int) -> void:
3: # Move Right
change_frame_order(1)
4: # Unlink Cel
var cel_index : int = Global.layers[layer].linked_cels.find(Global.canvases[frame])
var c = Global.canvases[frame]
var cel_index : int = Global.layers[layer].linked_cels.find(Global.frames[frame])
var f = Global.frames[frame]
var new_layers : Array = Global.layers.duplicate()
# Loop through the array to create new classes for each element, so that they
# won't be the same as the original array's classes. Needed for undo/redo to work properly.
for i in new_layers.size():
var new_linked_cels = new_layers[i].linked_cels.duplicate()
new_layers[i] = Layer.new(new_layers[i].name, new_layers[i].visible, new_layers[i].locked, new_layers[i].frame_container, new_layers[i].new_cels_linked, new_linked_cels)
var new_canvas_layers : Array = c.layers.duplicate()
for i in new_canvas_layers.size():
new_canvas_layers[i] = Cel.new(new_canvas_layers[i].image, new_canvas_layers[i].opacity)
var new_cels : Array = f.cels.duplicate()
for i in new_cels.size():
new_cels[i] = Cel.new(new_cels[i].image, new_cels[i].opacity)
if popup_menu.get_item_metadata(4) == "Unlink Cel":
new_layers[layer].linked_cels.remove(cel_index)
var sprite := Image.new()
sprite.copy_from(Global.canvases[frame].layers[layer].image)
sprite.copy_from(Global.frames[frame].cels[layer].image)
sprite.lock()
new_canvas_layers[layer].image = sprite
new_cels[layer].image = sprite
Global.undo_redo.create_action("Unlink Cel")
Global.undo_redo.add_do_property(Global, "layers", new_layers)
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
Global.undo_redo.add_do_property(f, "cels", new_cels)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_undo_property(c, "layers", c.layers)
Global.undo_redo.add_undo_property(f, "cels", f.cels)
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvases[frame]], layer)
Global.undo_redo.add_do_method(Global, "redo", [Global.canvases[frame]], layer)
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()
elif popup_menu.get_item_metadata(4) == "Link Cel":
new_layers[layer].linked_cels.append(Global.canvases[frame])
new_layers[layer].linked_cels.append(Global.frames[frame])
Global.undo_redo.create_action("Link Cel")
Global.undo_redo.add_do_property(Global, "layers", new_layers)
if new_layers[layer].linked_cels.size() > 1:
# If there are already linked cels, set the current cel's image
# to the first linked cel's image
new_canvas_layers[layer].image = new_layers[layer].linked_cels[0].layers[layer].image
new_canvas_layers[layer].image_texture = new_layers[layer].linked_cels[0].layers[layer].image_texture
Global.undo_redo.add_do_property(c, "layers", new_canvas_layers)
Global.undo_redo.add_undo_property(c, "layers", c.layers)
new_cels[layer].image = new_layers[layer].linked_cels[0].cels[layer].image
new_cels[layer].image_texture = new_layers[layer].linked_cels[0].cels[layer].image_texture
Global.undo_redo.add_do_property(f, "cels", new_cels)
Global.undo_redo.add_undo_property(f, "cels", f.cels)
Global.undo_redo.add_undo_property(Global, "layers", Global.layers)
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvases[frame]], layer)
Global.undo_redo.add_do_method(Global, "redo", [Global.canvases[frame]], layer)
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()
func change_frame_order(rate : int) -> void:
var change = frame + rate
var new_canvases : Array = Global.canvases.duplicate()
var temp = new_canvases[frame]
new_canvases[frame] = new_canvases[change]
new_canvases[change] = temp
var new_frames : Array = Global.frames.duplicate()
var temp = new_frames[frame]
new_frames[frame] = new_frames[change]
new_frames[change] = temp
Global.undo_redo.create_action("Change Frame Order")
Global.undo_redo.add_do_property(Global, "canvases", new_canvases)
Global.undo_redo.add_do_property(Global.canvases[frame], "frame", change)
Global.undo_redo.add_do_property(Global.canvases[change], "frame", frame)
Global.undo_redo.add_do_property(Global, "frames", new_frames)
if Global.current_frame == frame:
Global.undo_redo.add_do_property(Global, "current_frame", change)
Global.undo_redo.add_undo_property(Global, "current_frame", Global.current_frame)
Global.undo_redo.add_undo_property(Global, "canvases", Global.canvases)
Global.undo_redo.add_undo_property(Global.canvases[frame], "frame", frame)
Global.undo_redo.add_undo_property(Global.canvases[change], "frame", change)
Global.undo_redo.add_undo_property(Global, "frames", Global.frames)
Global.undo_redo.add_undo_method(Global, "undo", [Global.canvases[frame]])
Global.undo_redo.add_do_method(Global, "redo", [Global.canvases[frame]])
Global.undo_redo.add_undo_method(Global, "undo")
Global.undo_redo.add_do_method(Global, "redo")
Global.undo_redo.commit_action()

View file

@ -86,8 +86,8 @@ func _on_TagOptions_confirmed() -> void:
var tag_from : int = options_dialog.get_node("GridContainer/FromSpinBox").value
var tag_to : int = options_dialog.get_node("GridContainer/ToSpinBox").value
if tag_to > Global.canvases.size():
tag_to = Global.canvases.size()
if tag_to > Global.frames.size():
tag_to = Global.frames.size()
if tag_from > tag_to:
tag_from = tag_to

View file

@ -74,5 +74,6 @@ func _on_LockButton_pressed() -> void:
func _on_LinkButton_pressed() -> void:
Global.layers[i].new_cels_linked = !Global.layers[i].new_cels_linked
if Global.layers[i].new_cels_linked && !Global.layers[i].linked_cels:
Global.layers[i].linked_cels.append(Global.canvas)
# If button is pressed and there are no linked cels in the layer
Global.layers[i].linked_cels.append(Global.frames[Global.current_frame])
Global.layers[i].frame_container.get_child(Global.current_frame)._ready()