mirror of
https://github.com/tonytins/CozyPixelStudio.git
synced 2025-06-26 13:34:42 -04:00
Made a Frame class, no longer use multiple Canvases for multiple frames
The Canvas is now single node, instead of having multiple canvases for each frame. This should also be a performance optimization, since there are less canvas nodes, so there are less _input() calls. It should also fix a rare Undo/Redo issue with motion drawing and making lines. Could be unstable, needs more testing. As a side effect, the guides are now the same for all frames, so this should also close #246.
This commit is contained in:
parent
d8136a3e17
commit
54b628f6cb
17 changed files with 412 additions and 459 deletions
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue