diff --git a/Main.tscn b/Main.tscn index 55a5d8bac..176277b60 100644 --- a/Main.tscn +++ b/Main.tscn @@ -29,7 +29,6 @@ [ext_resource path="res://Prefabs/Dialogs/SplashDialog.tscn" type="PackedScene" id=27] [ext_resource path="res://Prefabs/Dialogs/CreateNewImage.tscn" type="PackedScene" id=28] [ext_resource path="res://Prefabs/Dialogs/ImportSprites.tscn" type="PackedScene" id=29] -[ext_resource path="res://Prefabs/Dialogs/ExportSprites.tscn" type="PackedScene" id=30] [ext_resource path="res://Prefabs/Dialogs/ScaleImage.tscn" type="PackedScene" id=31] [ext_resource path="res://Prefabs/Dialogs/PreferencesDialog.tscn" type="PackedScene" id=32] [ext_resource path="res://Prefabs/Dialogs/OutlineDialog.tscn" type="PackedScene" id=33] @@ -38,6 +37,7 @@ [ext_resource path="res://Prefabs/NewPaletteDialog.tscn" type="PackedScene" id=36] [ext_resource path="res://Prefabs/PaletteImportFileDialog.tscn" type="PackedScene" id=37] [ext_resource path="res://Prefabs/Dialogs/RotateImage.tscn" type="PackedScene" id=38] +[ext_resource path="res://Prefabs/Dialogs/ExportDialog.tscn" type="PackedScene" id=39] [sub_resource type="InputEventKey" id=1] scancode = 88 @@ -1259,7 +1259,7 @@ current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρι [node name="ImportSprites" parent="." instance=ExtResource( 29 )] -[node name="ExportSprites" parent="." instance=ExtResource( 30 )] +[node name="ExportDialog" parent="." instance=ExtResource( 39 )] [node name="ScaleImage" parent="." instance=ExtResource( 31 )] @@ -1360,7 +1360,7 @@ visible = false [connection signal="file_selected" from="SaveSprite" to="." method="_on_SaveSprite_file_selected"] [connection signal="popup_hide" from="SaveSprite" to="." method="_can_draw_true"] [connection signal="popup_hide" from="ImportSprites" to="." method="_can_draw_true"] -[connection signal="popup_hide" from="ExportSprites" to="." method="_can_draw_true"] +[connection signal="popup_hide" from="ExportDialog" to="." method="_can_draw_true"] [connection signal="popup_hide" from="ScaleImage" to="." method="_can_draw_true"] [connection signal="popup_hide" from="PreferencesDialog" to="." method="_can_draw_true"] [connection signal="popup_hide" from="OutlineDialog" to="." method="_can_draw_true"] diff --git a/Prefabs/Dialogs/ExportDialog.tscn b/Prefabs/Dialogs/ExportDialog.tscn new file mode 100644 index 000000000..5fdc4f53c --- /dev/null +++ b/Prefabs/Dialogs/ExportDialog.tscn @@ -0,0 +1,309 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Scripts/Dialogs/ExportDialog.gd" type="Script" id=1] + +[node name="ExportDialog" type="AcceptDialog"] +margin_right = 456.0 +margin_bottom = 530.0 +rect_min_size = Vector2( 456, 530 ) +window_title = "Export..." +resizable = true +dialog_hide_on_ok = false +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 448.0 +margin_bottom = 494.0 +rect_min_size = Vector2( 330, 0 ) +size_flags_vertical = 3 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Tabs" type="Tabs" parent="VBoxContainer"] +margin_right = 440.0 +margin_bottom = 24.0 +size_flags_vertical = 0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +margin_top = 28.0 +margin_right = 440.0 +margin_bottom = 32.0 + +[node name="PreviewLabel" type="Label" parent="VBoxContainer"] +margin_top = 36.0 +margin_right = 440.0 +margin_bottom = 50.0 +text = "Preview:" + +[node name="PreviewScroll" type="ScrollContainer" parent="VBoxContainer"] +margin_top = 54.0 +margin_right = 440.0 +margin_bottom = 358.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Previews" type="GridContainer" parent="VBoxContainer/PreviewScroll"] +margin_right = 440.0 +margin_bottom = 304.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 3 + +[node name="FrameOptions" type="VBoxContainer" parent="VBoxContainer"] +visible = false +margin_top = 334.0 +margin_right = 440.0 +margin_bottom = 358.0 + +[node name="FrameNumber" type="HBoxContainer" parent="VBoxContainer/FrameOptions"] +margin_right = 440.0 +margin_bottom = 24.0 + +[node name="FrameNumberLabel" type="Label" parent="VBoxContainer/FrameOptions/FrameNumber"] +margin_top = 5.0 +margin_right = 44.0 +margin_bottom = 19.0 +text = "Frame:" + +[node name="FrameNumber" type="SpinBox" parent="VBoxContainer/FrameOptions/FrameNumber"] +margin_left = 48.0 +margin_right = 440.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 100, 0 ) +size_flags_horizontal = 3 +min_value = 1.0 +page = 1.0 +value = 1.0 +rounded = true +align = 2 + +[node name="SpritesheetOptions" type="VBoxContainer" parent="VBoxContainer"] +margin_top = 362.0 +margin_right = 440.0 +margin_bottom = 386.0 + +[node name="Orientation" type="HBoxContainer" parent="VBoxContainer/SpritesheetOptions"] +margin_right = 440.0 +margin_bottom = 24.0 +alignment = 1 + +[node name="OrientationLabel" type="Label" parent="VBoxContainer/SpritesheetOptions/Orientation"] +margin_top = 5.0 +margin_right = 77.0 +margin_bottom = 19.0 +text = "Orientation:" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Orientation" type="OptionButton" parent="VBoxContainer/SpritesheetOptions/Orientation"] +margin_left = 81.0 +margin_right = 226.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +text = "Rows" +items = [ "Rows", null, false, 0, null, "Columns", null, false, 1, null ] +selected = 0 + +[node name="LinesCountLabel" type="Label" parent="VBoxContainer/SpritesheetOptions/Orientation"] +margin_left = 230.0 +margin_top = 5.0 +margin_right = 290.0 +margin_bottom = 19.0 +text = "Columns:" + +[node name="LinesCount" type="SpinBox" parent="VBoxContainer/SpritesheetOptions/Orientation"] +margin_left = 294.0 +margin_right = 440.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +min_value = 1.0 +max_value = 1000.0 +value = 1.0 +align = 2 + +[node name="AnimationOptions" type="VBoxContainer" parent="VBoxContainer"] +visible = false +margin_top = 366.0 +margin_right = 440.0 +margin_bottom = 386.0 + +[node name="AnimationType" type="OptionButton" parent="VBoxContainer/AnimationOptions"] +margin_right = 440.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +disabled = true +text = "All frames as multiple files" +items = [ "All frames as multiple files", null, false, 0, null ] +selected = 0 + +[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"] +margin_top = 390.0 +margin_right = 440.0 +margin_bottom = 394.0 + +[node name="Options" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 398.0 +margin_right = 440.0 +margin_bottom = 422.0 + +[node name="ResizeLabel" type="Label" parent="VBoxContainer/Options"] +margin_top = 5.0 +margin_right = 46.0 +margin_bottom = 19.0 +rect_min_size = Vector2( 30, 0 ) +text = "Resize:" +align = 2 + +[node name="Resize" type="SpinBox" parent="VBoxContainer/Options"] +margin_left = 50.0 +margin_right = 197.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +min_value = 10.0 +max_value = 1000.0 +step = 100.0 +value = 100.0 +align = 2 +suffix = "%" + +[node name="InterpolationLabel" type="Label" parent="VBoxContainer/Options"] +margin_left = 201.0 +margin_top = 5.0 +margin_right = 288.0 +margin_bottom = 19.0 +rect_min_size = Vector2( 30, 0 ) +text = "Interpolation:" +align = 2 + +[node name="Interpolation" type="OptionButton" parent="VBoxContainer/Options"] +margin_left = 292.0 +margin_right = 440.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +text = "Nearest" +align = 2 +items = [ "Nearest", null, false, 0, null, "Bilinear", null, false, 1, null, "Cubic", null, false, 2, null, "Trilinear", null, false, 3, null, "Lanczos", null, false, 4, null ] +selected = 0 + +[node name="HSeparator3" type="HSeparator" parent="VBoxContainer"] +margin_top = 426.0 +margin_right = 440.0 +margin_bottom = 430.0 + +[node name="Path" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 434.0 +margin_right = 440.0 +margin_bottom = 458.0 + +[node name="Label" type="Label" parent="VBoxContainer/Path"] +margin_top = 5.0 +margin_right = 32.0 +margin_bottom = 19.0 +rect_min_size = Vector2( 30, 0 ) +text = "Path:" + +[node name="PathLineEdit" type="LineEdit" parent="VBoxContainer/Path"] +margin_left = 36.0 +margin_right = 377.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +align = 2 + +[node name="PathButton" type="Button" parent="VBoxContainer/Path"] +margin_left = 381.0 +margin_right = 440.0 +margin_bottom = 24.0 +text = "Browse" + +[node name="File" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 462.0 +margin_right = 440.0 +margin_bottom = 486.0 + +[node name="Label" type="Label" parent="VBoxContainer/File"] +margin_top = 5.0 +margin_right = 30.0 +margin_bottom = 19.0 +rect_min_size = Vector2( 30, 0 ) +text = "File:" + +[node name="FileLineEdit" type="LineEdit" parent="VBoxContainer/File"] +margin_left = 34.0 +margin_right = 296.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +align = 2 + +[node name="FileFormat" type="OptionButton" parent="VBoxContainer/File"] +margin_left = 300.0 +margin_right = 440.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 130, 0 ) +disabled = true +text = ".png ; PNG Image" +items = [ ".png ; PNG Image", null, false, 0, null, ".gif ; GIF Image", null, true, 1, null ] +selected = 0 + +[node name="Popups" type="Node" parent="."] + +[node name="PathDialog" type="FileDialog" parent="Popups"] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 448.0 +margin_bottom = 494.0 +rect_min_size = Vector2( 440, 300 ) +size_flags_horizontal = 0 +size_flags_vertical = 0 +window_title = "Open a Directory" +resizable = true +mode = 2 +access = 2 +current_dir = "/home/novhack/Pixelorama" +current_path = "/home/novhack/Pixelorama/" + +[node name="PathValidationAlert" type="AcceptDialog" parent="Popups"] +margin_left = 8.0 +margin_top = 180.0 +margin_right = 448.0 +margin_bottom = 280.0 +size_flags_horizontal = 0 +size_flags_vertical = 0 +dialog_text = "Directory path or file name is not valid!" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="FileExistsAlert" type="AcceptDialog" parent="Popups"] +margin_left = 8.0 +margin_top = 180.0 +margin_right = 448.0 +margin_bottom = 280.0 +size_flags_horizontal = 0 +size_flags_vertical = 0 +dialog_text = "File %s already exists. Overwrite?" +__meta__ = { +"_edit_use_anchors_": false +} +[connection signal="about_to_show" from="." to="." method="_on_ExportDialog_about_to_show"] +[connection signal="confirmed" from="." to="." method="_on_ExportDialog_confirmed"] +[connection signal="custom_action" from="." to="." method="_on_ExportDialog_custom_action"] +[connection signal="tab_clicked" from="VBoxContainer/Tabs" to="." method="_on_Tabs_tab_clicked"] +[connection signal="value_changed" from="VBoxContainer/FrameOptions/FrameNumber/FrameNumber" to="." method="_on_Frame_value_changed"] +[connection signal="item_selected" from="VBoxContainer/SpritesheetOptions/Orientation/Orientation" to="." method="_on_Orientation_item_selected"] +[connection signal="value_changed" from="VBoxContainer/SpritesheetOptions/Orientation/LinesCount" to="." method="_on_LinesCount_value_changed"] +[connection signal="value_changed" from="VBoxContainer/Options/Resize" to="." method="_on_Resize_value_changed"] +[connection signal="item_selected" from="VBoxContainer/Options/Interpolation" to="." method="_on_Interpolation_item_selected"] +[connection signal="text_changed" from="VBoxContainer/Path/PathLineEdit" to="." method="_on_PathLineEdit_text_changed"] +[connection signal="pressed" from="VBoxContainer/Path/PathButton" to="." method="_on_PathButton_pressed"] +[connection signal="text_changed" from="VBoxContainer/File/FileLineEdit" to="." method="_on_FileLineEdit_text_changed"] +[connection signal="item_selected" from="VBoxContainer/File/FileFormat" to="." method="_on_FileFormat_item_selected"] +[connection signal="dir_selected" from="Popups/PathDialog" to="." method="_on_FileDialog_dir_selected"] +[connection signal="confirmed" from="Popups/FileExistsAlert" to="." method="_on_FileExistsAlert_confirmed"] +[connection signal="custom_action" from="Popups/FileExistsAlert" to="." method="_on_FileExistsAlert_custom_action"] diff --git a/Prefabs/Dialogs/ExportSprites.tscn b/Prefabs/Dialogs/ExportSprites.tscn deleted file mode 100644 index 6170e610d..000000000 --- a/Prefabs/Dialogs/ExportSprites.tscn +++ /dev/null @@ -1,95 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://Scripts/Dialogs/ExportSprites.gd" type="Script" id=1] - -[node name="ExportSprites" type="FileDialog"] -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -margin_left = -512.0 -margin_top = -300.0 -margin_right = 3.0 -margin_bottom = 48.0 -window_title = "Export Sprite as .png" -resizable = true -access = 2 -filters = PoolStringArray( "*.png ; PNG Image" ) -current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" -current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" -script = ExtResource( 1 ) - -[node name="ExportOption" type="OptionButton" parent="."] -margin_left = 8.0 -margin_top = 8.0 -margin_right = 507.0 -margin_bottom = 312.0 -text = "EXPORT_CURRENT_FRAME_LABEL" -items = [ "EXPORT_CURRENT_FRAME_LABEL", null, false, 0, null, "EXPORT_FRAMES_AS_MULTIPLE_FILES_LABEL", null, false, 1, null, "EXPORT_FRAMES_AS_SPRITESHEET_LABEL", null, false, 2, null ] -selected = 0 - -[node name="Resize" type="HBoxContainer" parent="."] -margin_left = 8.0 -margin_top = 8.0 -margin_right = 507.0 -margin_bottom = 312.0 - -[node name="Label" type="Label" parent="Resize"] -margin_top = 145.0 -margin_right = 46.0 -margin_bottom = 159.0 -text = "Resize:" - -[node name="ResizeValue" type="SpinBox" parent="Resize"] -margin_left = 50.0 -margin_right = 124.0 -margin_bottom = 304.0 -mouse_default_cursor_shape = 2 -min_value = 10.0 -max_value = 1000.0 -step = 10.0 -value = 100.0 -suffix = "%" - -[node name="Label2" type="Label" parent="Resize"] -margin_left = 128.0 -margin_top = 145.0 -margin_right = 215.0 -margin_bottom = 159.0 -text = "Interpolation:" - -[node name="Interpolation" type="OptionButton" parent="Resize"] -margin_left = 219.0 -margin_right = 298.0 -margin_bottom = 304.0 -text = "Nearest" -items = [ "Nearest", null, false, 0, null, "Bilinear", null, false, 1, null, "Cubic", null, false, 2, null, "Trilinear", null, false, 3, null, "Lanczos", null, false, 4, null ] -selected = 0 - -[node name="Spritesheet" type="HBoxContainer" parent="."] -visible = false -margin_left = 8.0 -margin_top = 8.0 -margin_right = 507.0 -margin_bottom = 312.0 - -[node name="ColumnsOrRows" type="OptionButton" parent="Spritesheet"] -margin_right = 97.0 -margin_bottom = 304.0 -text = "Columns" -items = [ "Columns", null, false, 0, null, "Rows", null, false, 1, null ] -selected = 0 - -[node name="Frames" type="SpinBox" parent="Spritesheet"] -margin_left = 101.0 -margin_right = 175.0 -margin_bottom = 304.0 -mouse_default_cursor_shape = 2 -min_value = 1.0 -value = 1.0 -[connection signal="file_selected" from="." to="." method="_on_ExportSprites_file_selected"] -[connection signal="item_selected" from="ExportOption" to="." method="_on_ExportOption_item_selected"] -[connection signal="value_changed" from="Resize/ResizeValue" to="." method="_on_ResizeValue_value_changed"] -[connection signal="item_selected" from="Resize/Interpolation" to="." method="_on_Interpolation_item_selected"] -[connection signal="item_selected" from="Spritesheet/ColumnsOrRows" to="." method="_on_ColumnsOrRows_item_selected"] -[connection signal="value_changed" from="Spritesheet/Frames" to="." method="_on_Frames_value_changed"] diff --git a/Scripts/Dialogs/ExportDialog.gd b/Scripts/Dialogs/ExportDialog.gd new file mode 100644 index 000000000..e06661ec9 --- /dev/null +++ b/Scripts/Dialogs/ExportDialog.gd @@ -0,0 +1,406 @@ +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 +var processed_images = [] # Image[] + +# Frame options +var frame_number := 0 + +# Spritesheet options +enum Orientation { Rows = 0, Columns = 1 } +var orientation : int = Orientation.Rows +# How many rows/columns before new line is added +var lines_count := 1 + +# Animation options +enum AnimationType { MultipleFiles = 0 } +var animation_type : int = AnimationType.MultipleFiles + +# Options +var resize := 100 +var interpolation := 0 # Image.Interpolation + +# Export directory path and export file name +var directory_path := "" +var file_name := "" +var file_format := ".png" + +var file_exists_alert = "File %s already exists. Overwrite?" + +# Store all settings after export, enables a quick re-export with same settings +var was_exported : bool = false +var exported_tab : int +var exported_frame_number : int +var exported_orientation : int +var exported_lines_count : int +var exported_animation_type : int +var exported_resize : int +var exported_interpolation : int +var exported_directory_path : String +var exported_file_name : String +var exported_file_format : String + +# Export coroutine signal +signal resume_export_function() +var stop_export = false + +func _ready(): + $VBoxContainer/Tabs.add_tab("Frame") + $VBoxContainer/Tabs.add_tab("Spritesheet") + $VBoxContainer/Tabs.add_tab("Animation") + add_button("Cancel", false, "cancel") + $Popups/FileExistsAlert.add_button("Cancel Export", false, "cancel") + + +func show_tab(): + $VBoxContainer/FrameOptions.hide() + $VBoxContainer/SpritesheetOptions.hide() + $VBoxContainer/AnimationOptions.hide() + + match current_tab: + ExportTab.Frame: + if not was_exported: + frame_number = Global.current_frame + 1 + $VBoxContainer/FrameOptions/FrameNumber/FrameNumber.max_value = Global.canvases.size() + 1 + $VBoxContainer/FrameOptions/FrameNumber/FrameNumber.value = frame_number + process_frame() + $VBoxContainer/FrameOptions.show() + ExportTab.Spritesheet: + if not was_exported: + orientation = Orientation.Rows + lines_count = int(ceil(sqrt(Global.canvases.size()))) + $VBoxContainer/SpritesheetOptions/Orientation/Orientation.selected = orientation + $VBoxContainer/SpritesheetOptions/Orientation/LinesCount.max_value = Global.canvases.size() + $VBoxContainer/SpritesheetOptions/Orientation/LinesCount.value = lines_count + $VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:" + process_spritesheet() + $VBoxContainer/SpritesheetOptions.show() + ExportTab.Animation: + process_animation() + $VBoxContainer/AnimationOptions.show() + set_preview() + $VBoxContainer/Tabs.current_tab = current_tab + + +func external_export() -> void: + restore_previous_export_settings() + match current_tab: + ExportTab.Frame: + process_frame() + ExportTab.Spritesheet: + process_spritesheet() + ExportTab.Animation: + process_animation() + export_processed_images(true) + + +func process_frame() -> void: + var canvas = Global.canvases[frame_number - 1] + var image := Image.new() + image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8) + blend_layers(image, canvas) + processed_images.clear() + processed_images.append(image) + + +func process_spritesheet() -> void: + # 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() + var spritesheet_rows = lines_count if orientation == Orientation.Columns else frames_divided_by_spritesheet_lines() + + var width = Global.canvas.size.x * spritesheet_columns + var height = Global.canvas.size.y * spritesheet_rows + + var whole_image := Image.new() + whole_image.create(width, height, false, Image.FORMAT_RGBA8) + whole_image.lock() + var origin := Vector2.ZERO + var hh := 0 + var vv := 0 + for canvas in Global.canvases: + if orientation == Orientation.Rows: + if vv < spritesheet_columns: + origin.x = canvas.size.x * vv + vv += 1 + else: + hh += 1 + origin.x = 0 + vv = 1 + origin.y = canvas.size.y * hh + else: + if hh < spritesheet_rows: + origin.y = 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) + + processed_images.clear() + processed_images.append(whole_image) + + +func process_animation() -> void: + processed_images.clear() + for canvas in Global.canvases: + var image := Image.new() + image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8) + blend_layers(image, canvas) + processed_images.append(image) + + +func set_preview() -> void: + remove_previews() + if processed_images.size() == 1 and current_tab != ExportTab.Animation: + $VBoxContainer/PreviewScroll/Previews.columns = 1 + add_preview(processed_images[0]) + else: + $VBoxContainer/PreviewScroll/Previews.columns = ceil(sqrt(processed_images.size())) + for i in range(processed_images.size()): + add_preview(processed_images[i], i + 1) + + +func add_preview(image: Image, canvas_number: int = -1): + var container = VBoxContainer.new() + container.size_flags_horizontal = SIZE_EXPAND_FILL + container.size_flags_vertical = SIZE_EXPAND_FILL + container.rect_min_size = Vector2(0, 128) + + var preview = TextureRect.new() + preview.expand = true + preview.size_flags_horizontal = SIZE_EXPAND_FILL + preview.size_flags_vertical = SIZE_EXPAND_FILL + preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + preview.texture = ImageTexture.new() + preview.texture.create_from_image(image, 0) + + container.add_child(preview) + + if canvas_number != -1: + var label = Label.new() + label.align = Label.ALIGN_CENTER + label.text = String(canvas_number) + container.add_child(label) + + $VBoxContainer/PreviewScroll/Previews.add_child(container) + + +func remove_previews(): + for child in $VBoxContainer/PreviewScroll/Previews.get_children(): + child.queue_free() + + +func export_processed_images(ignore_overwrites: bool) -> void: + # Stop export if directory path or file name are not valid + var dir = Directory.new() + if not dir.dir_exists(directory_path) or not file_name.is_valid_filename(): + $Popups/PathValidationAlert.popup_centered() + return + + # Check export paths + var export_paths = [] + for i in range(processed_images.size()): + stop_export = false + var export_path = create_export_path(true if current_tab == ExportTab.Animation else false, i + 1) + # Check if the file already exists + var fileCheck = File.new() + if fileCheck.file_exists(export_path): + # Ask user if he want's to overwrite the file + if not was_exported or (was_exported and not ignore_overwrites): + # Overwrite existing file? + $Popups/FileExistsAlert.dialog_text = file_exists_alert % export_path + $Popups/FileExistsAlert.popup_centered() + # Stops the function until the user decides if he want's to overwrite + yield(self, "resume_export_function") + if stop_export: + # User decided to stop export + return + export_paths.append(export_path) + + # Scale images that are to export + scale_processed_images() + + for i in range(processed_images.size()): + var err = processed_images[i].save_png(export_paths[i]) + if err != OK: + OS.alert("Can't save file") + + # Store settings for quick export and when the dialog is opened again + was_exported = true + store_export_settings() + Global.file_menu.get_popup().set_item_text(5, tr("Export") + " %s" % (file_name + file_format)) + Global.notification_label("File(s) exported") + hide() + + +# Blends canvas layers into passed image starting from the origin position +func blend_layers(image: Image, canvas: Canvas, origin: Vector2 = Vector2(0, 0)): + image.lock() + var layer_i := 0 + for layer in canvas.layers: + if Global.layers[layer_i][1]: + var layer_image : Image = layer[0] + layer_image.lock() + if layer[2] < 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[4] + layer_image.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)) + canvas.blend_rect(image, layer_image, Rect2(canvas.position, canvas.size), origin) + layer_i += 1 + image.unlock() + + +func scale_processed_images(): + for processed_image in processed_images: + if resize != 100: + processed_image.unlock() + processed_image.resize(processed_image.get_size().x * resize / 100, processed_image.get_size().y * resize / 100, interpolation) + + +func create_export_path(multifile: bool, frame: int = 0) -> String: + var path = file_name + # Only append frame number when there are multiple files exported + if multifile: + path += "_" + String(frame) + + return directory_path.plus_file(path + file_format) + + +func frames_divided_by_spritesheet_lines() -> int: + return int(ceil(Global.canvases.size() / float(lines_count))) + + +func store_export_settings(): + exported_tab = current_tab + exported_frame_number = frame_number + exported_orientation = orientation + exported_lines_count = lines_count + exported_animation_type = animation_type + exported_resize = resize + exported_interpolation = interpolation + exported_directory_path = directory_path + exported_file_name = file_name + exported_file_format = file_format + +# Fill the dialog with previous export settings +func restore_previous_export_settings(): + current_tab = exported_tab + frame_number = exported_frame_number if exported_frame_number <= Global.canvases.size() else Global.canvases.size() + orientation = exported_orientation + lines_count = exported_lines_count + animation_type = exported_animation_type + resize = exported_resize + interpolation = exported_interpolation + directory_path = exported_directory_path + file_name = exported_file_name + file_format = exported_file_format + + +func _on_ExportDialog_about_to_show(): + # If export already occured - fill the dialog with previous export settings + if was_exported: + restore_previous_export_settings() + + if directory_path.empty(): + directory_path = OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP) + + # If export already occured - sets gui to show previous settings + $VBoxContainer/Options/Resize.value = resize + $VBoxContainer/Options/Interpolation.selected = interpolation + $VBoxContainer/Path/PathLineEdit.text = directory_path + $VBoxContainer/File/FileLineEdit.text = file_name + $VBoxContainer/File/FileFormat.text = file_format + show_tab() + + +func _on_Tabs_tab_clicked(tab): + current_tab = tab + show_tab() + + +func _on_Frame_value_changed(value: float): + frame_number = value + process_frame() + set_preview() + + +func _on_Orientation_item_selected(id): + orientation = id + if orientation == Orientation.Rows: + $VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Columns:" + else: + $VBoxContainer/SpritesheetOptions/Orientation/LinesCountLabel.text = "Rows:" + $VBoxContainer/SpritesheetOptions/Orientation/LinesCount.value = frames_divided_by_spritesheet_lines() + process_spritesheet() + set_preview() + + +func _on_LinesCount_value_changed(value): + lines_count = value + process_spritesheet() + set_preview() + + +func _on_Resize_value_changed(value: float) -> void: + resize = value + + +func _on_Interpolation_item_selected(id: int) -> void: + interpolation = id + + +func _on_ExportDialog_confirmed(): + export_processed_images(false) + + +func _on_ExportDialog_custom_action(action): + if action == "cancel": + hide() + + +func _on_PathButton_pressed(): + $Popups/PathDialog.popup_centered() + + +func _on_PathLineEdit_text_changed(new_text): + directory_path = new_text + + +func _on_FileLineEdit_text_changed(new_text): + file_name = new_text + + +func _on_FileDialog_dir_selected(dir: String): + $VBoxContainer/Path/PathLineEdit.text = dir + directory_path = dir + + +func _on_FileFormat_item_selected(id): + match id: + 0: # PNG + file_format = '.png'; + 1: # GIF + file_format = '.gif'; + + +func _on_FileExistsAlert_confirmed(): + # Overwrite existing file + $Popups/FileExistsAlert.dialog_text = file_exists_alert + stop_export = false + emit_signal("resume_export_function") + + +func _on_FileExistsAlert_custom_action(action): + if action == "cancel": + # Cancel export + $Popups/FileExistsAlert.dialog_text = file_exists_alert + stop_export = true + emit_signal("resume_export_function") + $Popups/FileExistsAlert.hide() diff --git a/Scripts/Dialogs/ExportSprites.gd b/Scripts/Dialogs/ExportSprites.gd deleted file mode 100644 index 40cfdb1a5..000000000 --- a/Scripts/Dialogs/ExportSprites.gd +++ /dev/null @@ -1,159 +0,0 @@ -extends FileDialog - -var current_export_path := "" -var export_option := 0 -var resize := 100 -var interpolation = Image.INTERPOLATE_NEAREST -var frames_spinbox : SpinBox -var per_rows := false -var spritesheet_rows = 1 -var spritesheet_columns = 1 - -func _ready() -> void: - frames_spinbox = Global.find_node_by_name(self, "Frames") - var children := [] - for i in range(get_child_count()): - if i > 7: - children.append(get_child(i)) - - for child in children: - remove_child(child) - get_vbox().add_child(child) - -func _on_ExportOption_item_selected(ID : int) -> void: - export_option = ID - var spritesheet_container = Global.find_node_by_name(self, "Spritesheet") - if ID > 1: - spritesheet_container.visible = true - else: - spritesheet_container.visible = false - -func _on_ResizeValue_value_changed(value) -> void: - resize = value - -func _on_Interpolation_item_selected(ID : int) -> void: - interpolation = ID - -func _on_ColumnsOrRows_item_selected(ID) -> void: - per_rows = bool(ID) - # Update spritesheet_rows/columns variable - _on_Frames_value_changed(frames_spinbox.value) - -func _on_Frames_value_changed(value): - value = min(value, Global.canvases.size()) - - if per_rows: - spritesheet_rows = value - frames_spinbox.value = spritesheet_rows - else: - spritesheet_columns = value - frames_spinbox.value = spritesheet_columns - -func _on_ExportSprites_file_selected(path : String) -> void: - current_export_path = path - Global.file_menu.get_popup().set_item_text(5, tr("Export") + " %s" % path.get_file()) - export_project() - -func export_project() -> void: - if export_option == 0: # Export current frame - save_sprite(Global.canvas, current_export_path) - elif export_option == 1: # Export all frames as multiple files - var i := 1 - for canvas in Global.canvases: - var path := "%s_%s" % [current_export_path, str(i)] - path = path.replace(".png", "") - path = "%s.png" % path - save_sprite(canvas, path) - i += 1 - elif export_option == 2: # Export all frames as a spritesheet (single file) - save_spritesheet() - - Global.notification_label("File exported") - -func save_sprite(canvas : Canvas, path : String) -> void: - var whole_image := Image.new() - whole_image.create(canvas.size.x, canvas.size.y, false, Image.FORMAT_RGBA8) - whole_image.lock() - var layer_i := 0 - for layer in canvas.layers: - if Global.layers[layer_i][1]: # If layer is visible - var img : Image = layer[0] - img.lock() - if layer[2] < 1: # If we have layer transparency - for xx in img.get_size().x: - for yy in img.get_size().y: - var pixel_color := img.get_pixel(xx, yy) - var alpha : float = pixel_color.a * layer[4] - img.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)) - - canvas.blend_rect(whole_image, img, Rect2(canvas.position, canvas.size), Vector2.ZERO) - layer[0].lock() - - layer_i += 1 - - if resize != 100: - whole_image.unlock() - whole_image.resize(whole_image.get_size().x * resize / 100, whole_image.get_size().y * resize / 100, interpolation) - var err = whole_image.save_png(path) - if err != OK: - OS.alert("Can't save file") - -func save_spritesheet() -> void: - if per_rows: - spritesheet_columns = ceil(Global.canvases.size() / spritesheet_rows) - else: - spritesheet_rows = ceil(Global.canvases.size() / spritesheet_columns) - var width = Global.canvas.size.x * spritesheet_rows - var height = Global.canvas.size.y * spritesheet_columns - - var whole_image := Image.new() - whole_image.create(width, height, false, Image.FORMAT_RGBA8) - whole_image.lock() - var dst := Vector2.ZERO - var hh := 0 - var vv := 0 - for canvas in Global.canvases: - if per_rows: - if vv < spritesheet_columns: - dst.y = canvas.size.y * vv - vv += 1 - else: - hh += 1 - dst.y = 0 - vv = 1 - dst.x = canvas.size.x * hh - - else: - if hh < spritesheet_rows: - dst.x = canvas.size.x * hh - hh += 1 - else: - vv += 1 - dst.x = 0 - hh = 1 - dst.y = canvas.size.y * vv - - var layer_i := 0 - for layer in canvas.layers: - if Global.layers[layer_i][1]: # If layer is visible - var img : Image = layer[0] - img.lock() - if layer[2] < 1: # If we have layer transparency - for xx in img.get_size().x: - for yy in img.get_size().y: - var pixel_color := img.get_pixel(xx, yy) - var alpha : float = pixel_color.a * layer[4] - img.set_pixel(xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)) - - canvas.blend_rect(whole_image, img, Rect2(canvas.position, canvas.size), dst) - layer[0].lock() - - layer_i += 1 - - if resize != 100: - whole_image.unlock() - whole_image.resize(width * resize / 100, height * resize / 100, interpolation) - var err = whole_image.save_png(current_export_path) - if err != OK: - OS.alert("Can't save file") - diff --git a/Scripts/Main.gd b/Scripts/Main.gd index 807aa6019..eff0c1bcd 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -45,9 +45,9 @@ func _ready() -> void: "Open..." : KEY_MASK_CMD + KEY_O, "Save..." : KEY_MASK_CMD + KEY_S, "Save as..." : KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S, - "Import PNG..." : KEY_MASK_CMD + KEY_I, - "Export PNG..." : KEY_MASK_CMD + KEY_E, - "Export PNG as..." : KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_E, + "Import..." : KEY_MASK_CMD + KEY_I, + "Export..." : KEY_MASK_CMD + KEY_E, + "Export as..." : KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_E, "Quit" : KEY_MASK_CMD + KEY_Q } var edit_menu_items := { @@ -238,13 +238,13 @@ func file_menu_id_pressed(id : int) -> void: Global.can_draw = false opensprite_file_selected = false 5: # Export - if $ExportSprites.current_export_path == "": - $ExportSprites.popup_centered() + if $ExportDialog.was_exported == false: + $ExportDialog.popup_centered() Global.can_draw = false else: - $ExportSprites.export_project() + $ExportDialog.external_export() 6: # Export as - $ExportSprites.popup_centered() + $ExportDialog.popup_centered() Global.can_draw = false 7: # Quit show_quit_dialog() @@ -510,20 +510,21 @@ func _on_OpenSprite_file_selected(path : String) -> void: current_save_path = path $SaveSprite.current_path = path - $ExportSprites.current_export_path = path.trim_suffix(".pxo") + ".png" - $ExportSprites.current_path = $ExportSprites.current_export_path + $ExportDialog.file_name = path.get_file().trim_suffix(".pxo") + $ExportDialog.directory_path = path.get_base_dir() + $ExportDialog.was_exported = false file_menu.set_item_text(2, tr("Save") + " %s" % path.get_file()) - file_menu.set_item_text(5, tr("Export") + " %s" % $ExportSprites.current_path.get_file()) + file_menu.set_item_text(5, tr("Export")) Global.window_title = path.get_file() + " - Pixelorama" func _on_SaveSprite_file_selected(path : String) -> void: current_save_path = path - $ExportSprites.current_export_path = path.trim_suffix(".pxo") + ".png" - $ExportSprites.current_path = $ExportSprites.current_export_path + $ExportDialog.file_name = path.get_file().trim_suffix(".pxo") + $ExportDialog.directory_path = path.get_base_dir() + $ExportDialog.was_exported = false file_menu.set_item_text(2, tr("Save") + " %s" % path.get_file()) - file_menu.set_item_text(5, tr("Export") + " %s" % $ExportSprites.current_path.get_file()) var file := File.new() var err := file.open(path, File.WRITE) if err == OK: @@ -593,9 +594,8 @@ func clear_canvases() -> void: child.queue_free() Global.canvases.clear() current_save_path = "" - $ExportSprites.current_export_path = "" file_menu.set_item_text(2, "Save") - file_menu.set_item_text(5, "Export PNG...") + file_menu.set_item_text(5, "Export...") Global.window_title = "(" + tr("untitled") + ") - Pixelorama" Global.undo_redo.clear_history(false)