diff --git a/src/Autoload/Export.gd b/src/Autoload/Export.gd index fabd98292..ec8370229 100644 --- a/src/Autoload/Export.gd +++ b/src/Autoload/Export.gd @@ -30,8 +30,7 @@ var custom_exporter_generators := {} var current_tab := ExportTab.IMAGE ## All frames and their layers processed/blended into images -var processed_images: Array[Image] = [] -var durations: PackedFloat32Array = [] +var processed_images: Array[ProcessedImage] = [] # Spritesheet options var orientation := Orientation.COLUMNS @@ -58,6 +57,15 @@ var export_progress := 0.0 @onready var gif_export_thread := Thread.new() +class ProcessedImage: + var image: Image + var duration: float + + func _init(_image: Image, _duration := 1.0) -> void: + image = _image + duration = _duration + + func _exit_tree() -> void: if gif_export_thread.is_started(): gif_export_thread.wait_to_finish() @@ -225,18 +233,17 @@ func process_spritesheet(project := Global.current_project) -> void: tag_origins[0] += 1 _blend_layers(whole_image, frame, origin) - processed_images.append(whole_image) + processed_images.append(ProcessedImage.new(whole_image)) func process_animation(project := Global.current_project) -> void: processed_images.clear() - durations.clear() var frames := _calculate_frames(project) for frame in frames: var image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8) _blend_layers(image, frame) - processed_images.append(image) - durations.append(frame.duration * (1.0 / project.fps)) + var duration := frame.duration * (1.0 / project.fps) + processed_images.append(ProcessedImage.new(image, duration)) func _calculate_frames(project := Global.current_project) -> Array[Frame]: @@ -320,7 +327,6 @@ func export_processed_images( var result := true var details := { "processed_images": processed_images, - "durations": durations, "export_dialog": export_dialog, "export_paths": export_paths, "project": project @@ -368,19 +374,19 @@ func export_processed_images( if OS.has_feature("web"): if project.file_format == FileFormat.PNG: JavaScriptBridge.download_buffer( - processed_images[i].save_png_to_buffer(), + processed_images[i].image.save_png_to_buffer(), export_paths[i].get_file(), "image/png" ) elif project.file_format == FileFormat.WEBP: JavaScriptBridge.download_buffer( - processed_images[i].save_webp_to_buffer(), + processed_images[i].image.save_webp_to_buffer(), export_paths[i].get_file(), "image/webp" ) elif project.file_format == FileFormat.JPEG: JavaScriptBridge.download_buffer( - processed_images[i].save_jpg_to_buffer(), + processed_images[i].image.save_jpg_to_buffer(), export_paths[i].get_file(), "image/jpeg" ) @@ -388,11 +394,11 @@ func export_processed_images( else: var err: Error if project.file_format == FileFormat.PNG: - err = processed_images[i].save_png(export_paths[i]) + err = processed_images[i].image.save_png(export_paths[i]) elif project.file_format == FileFormat.WEBP: - err = processed_images[i].save_webp(export_paths[i]) + err = processed_images[i].image.save_webp(export_paths[i]) elif project.file_format == FileFormat.JPEG: - err = processed_images[i].save_jpg(export_paths[i]) + err = processed_images[i].image.save_jpg(export_paths[i]) if err != OK: Global.popup_error( tr("File failed to save. Error code %s (%s)") % [err, error_string(err)] @@ -425,9 +431,9 @@ func export_video(export_paths: PackedStringArray) -> bool: for i in range(processed_images.size()): var temp_file_name := str(i + 1).pad_zeros(number_of_digits) + ".png" var temp_file_path := temp_path_real.path_join(temp_file_name) - processed_images[i].save_png(temp_file_path) + processed_images[i].image.save_png(temp_file_path) input_file.store_line("file '" + temp_file_name + "'") - input_file.store_line("duration %s" % durations[i]) + input_file.store_line("duration %s" % processed_images[i].duration) input_file.close() var ffmpeg_execute: PackedStringArray = [ "-y", "-f", "concat", "-i", input_file_path, export_paths[0] @@ -464,8 +470,8 @@ func export_animated(args: Dictionary) -> void: var frames := [] for i in range(processed_images.size()): var frame: AImgIOFrame = AImgIOFrame.new() - frame.content = processed_images[i] - frame.duration = durations[i] + frame.content = processed_images[i].image + frame.duration = processed_images[i].duration frames.push_back(frame) # Export and save GIF/APNG @@ -489,13 +495,12 @@ func _increase_export_progress(export_dialog: Node) -> void: func _scale_processed_images() -> void: + var resize_f := resize / 100.0 for processed_image in processed_images: - if resize != 100: - processed_image.resize( - processed_image.get_size().x * resize / 100, - processed_image.get_size().y * resize / 100, - interpolation - ) + if is_equal_approx(resize, 1.0): + continue + var image := processed_image.image + image.resize(image.get_size().x * resize_f, image.get_size().y * resize_f, interpolation) func file_format_string(format_enum: int) -> String: diff --git a/src/UI/Dialogs/ExportDialog.gd b/src/UI/Dialogs/ExportDialog.gd index fe16473df..916e09b91 100644 --- a/src/UI/Dialogs/ExportDialog.gd +++ b/src/UI/Dialogs/ExportDialog.gd @@ -2,7 +2,7 @@ extends ConfirmationDialog ## Called when user resumes export after filename collision signal resume_export_function -signal about_to_preview(Dictionary) +signal about_to_preview(dict: Dictionary) var preview_current_frame := 0 var preview_frames: Array[Texture2D] = [] @@ -24,8 +24,7 @@ var spritesheet_exports: Array[Export.FileFormat] = [ Export.FileFormat.PNG, Export.FileFormat.WEBP, Export.FileFormat.JPEG ] -var _preview_images: Array[Image] -var _preview_durations: PackedFloat32Array +var _preview_images: Array[Export.ProcessedImage] @onready var tabs: TabBar = $VBoxContainer/TabBar @onready var checker: ColorRect = $"%TransparentChecker" @@ -97,19 +96,17 @@ func show_tab() -> void: func set_preview() -> void: - _preview_images = Export.processed_images.duplicate() - _preview_durations = Export.durations.duplicate() + _preview_images = Export.processed_images var preview_data = { "exporter_id": Global.current_project.file_format, "export_tab": Export.current_tab, "preview_images": _preview_images, - "durations": _preview_durations } about_to_preview.emit(preview_data) remove_previews() if _preview_images.size() == 1: previews.columns = 1 - add_image_preview(_preview_images[0]) + add_image_preview(_preview_images[0].image) else: if Export.is_single_file_format(): previews.columns = 1 @@ -117,7 +114,7 @@ func set_preview() -> void: else: previews.columns = ceili(sqrt(_preview_images.size())) for i in range(_preview_images.size()): - add_image_preview(_preview_images[i], i + 1) + add_image_preview(_preview_images[i].image, i + 1) func add_image_preview(image: Image, canvas_number: int = -1) -> void: @@ -140,7 +137,7 @@ func add_animated_preview() -> void: preview_frames = [] for processed_image in _preview_images: - var texture := ImageTexture.create_from_image(processed_image) + var texture := ImageTexture.create_from_image(processed_image.image) preview_frames.push_back(texture) var container := create_preview_container() @@ -152,7 +149,7 @@ func add_animated_preview() -> void: previews.add_child(container) frame_timer.set_one_shot(true) # wait_time can't change correctly if the timer is playing - frame_timer.wait_time = _preview_durations[preview_current_frame] + frame_timer.wait_time = _preview_images[preview_current_frame].duration frame_timer.start() @@ -239,7 +236,7 @@ func create_layer_list() -> void: func update_dimensions_label() -> void: if _preview_images.size() > 0: - var new_size: Vector2 = _preview_images[0].get_size() * (Export.resize / 100.0) + var new_size: Vector2 = _preview_images[0].image.get_size() * (Export.resize / 100.0) dimension_label.text = str(new_size.x, "×", new_size.y) @@ -413,7 +410,7 @@ func _on_FrameTimer_timeout() -> void: else: preview_current_frame += 1 - frame_timer.wait_time = _preview_durations[preview_current_frame - 1] + frame_timer.wait_time = _preview_images[preview_current_frame - 1].duration frame_timer.start()