1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-19 09:39:48 +00:00
Pixelorama/Scripts/Dialogs/ExportDialog.gd
OverloadedOrama 9bf1660d18 ExportDialog visual changes
Tabs are now included as Class items in all themes, the OK button in ExportDialog appears on the left on Windows machines, and on the right in all other operating systems. Also added border outlines to all window dialogs.
2020-03-25 18:22:29 +02:00

415 lines
13 KiB
GDScript

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() -> void:
$VBoxContainer/Tabs.add_tab("Frame")
$VBoxContainer/Tabs.add_tab("Spritesheet")
$VBoxContainer/Tabs.add_tab("Animation")
if OS.get_name() == "Windows":
add_button("Cancel", true, "cancel")
$Popups/FileExistsAlert.add_button("Cancel Export", true, "cancel")
else:
add_button("Cancel", false, "cancel")
$Popups/FileExistsAlert.add_button("Cancel Export", false, "cancel")
func show_tab() -> void:
$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) -> void:
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() -> void:
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)) -> void:
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[2]
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() -> void:
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() -> void:
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() -> void:
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() -> void:
# 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()
for child in $Popups.get_children(): # Set the theme for the popups
child.theme = Global.control.theme
file_exists_alert = tr("File %s already exists. Overwrite?") # Update translation
#$VBoxContainer/Tabs.set_tab_title(0, "Frame")
func _on_Tabs_tab_clicked(tab : int) -> void:
current_tab = tab
show_tab()
func _on_Frame_value_changed(value: float) -> void:
frame_number = value
process_frame()
set_preview()
func _on_Orientation_item_selected(id : int) -> void:
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 : float) -> void:
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() -> void:
export_processed_images(false)
func _on_ExportDialog_custom_action(action : String) -> void:
if action == "cancel":
hide()
func _on_PathButton_pressed() -> void:
$Popups/PathDialog.popup_centered()
func _on_PathLineEdit_text_changed(new_text : String) -> void:
directory_path = new_text
func _on_FileLineEdit_text_changed(new_text : String) -> void:
file_name = new_text
func _on_FileDialog_dir_selected(dir : String) -> void:
$VBoxContainer/Path/PathLineEdit.text = dir
directory_path = dir
func _on_FileFormat_item_selected(id : int) -> void:
match id:
0: # PNG
file_format = '.png'
1: # GIF
file_format = '.gif'
func _on_FileExistsAlert_confirmed() -> void:
# Overwrite existing file
$Popups/FileExistsAlert.dialog_text = file_exists_alert
stop_export = false
emit_signal("resume_export_function")
func _on_FileExistsAlert_custom_action(action : String) -> void:
if action == "cancel":
# Cancel export
$Popups/FileExistsAlert.dialog_text = file_exists_alert
stop_export = true
emit_signal("resume_export_function")
$Popups/FileExistsAlert.hide()