1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00

Re-implement the ability to add custom export formats from extensions and allow preview display (#924)

* Fixed ExportApi

* Added ability for custom exporters to show their preview

* renamed a function

* Formatting

* Linting
This commit is contained in:
Variable 2023-10-18 18:06:19 +05:00 committed by GitHub
parent 26e843301f
commit 5e64491bee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 59 deletions

View file

@ -10,6 +10,7 @@ enum FileFormat { PNG, WEBP, JPEG, GIF, APNG }
var animated_formats := [FileFormat.GIF, FileFormat.APNG]
## A dictionary of custom exporter generators (received from extensions)
var custom_file_formats := {}
var custom_exporter_generators := {}
var current_tab := ExportTab.IMAGE
@ -51,16 +52,43 @@ func _multithreading_enabled() -> bool:
return ProjectSettings.get_setting("rendering/driver/threads/thread_model") == 2
func add_file_format(_format: String) -> int:
var id := FileFormat.size()
# FileFormat.merge({format: id})
func add_custom_file_format(
format_name: String, extension: String, exporter_generator: Object, tab: int, is_animated: bool
) -> int:
# Obtain a unique id
var id := Export.FileFormat.size()
for i in Export.custom_file_formats.size():
var format_id = id + i
if !Export.custom_file_formats.values().has(i):
id = format_id
# Add to custom_file_formats
custom_file_formats.merge({format_name: id})
custom_exporter_generators.merge({id: [exporter_generator, extension]})
if is_animated:
Export.animated_formats.append(id)
# Add to export dialog
match tab:
ExportTab.IMAGE:
Global.export_dialog.image_exports.append(id)
ExportTab.SPRITESHEET:
Global.export_dialog.spritesheet_exports.append(id)
_: # Both
Global.export_dialog.image_exports.append(id)
Global.export_dialog.spritesheet_exports.append(id)
return id
func remove_file_format(id: int) -> void:
for key in Export.FileFormat.keys():
if Export.FileFormat[key] == id:
# Export.FileFormat.erase(key)
func remove_custom_file_format(id: int) -> void:
for key in custom_file_formats.keys():
if custom_file_formats[key] == id:
custom_file_formats.erase(key)
# remove exporter generator
Export.custom_exporter_generators.erase(id)
# remove from animated (if it is present there)
Export.animated_formats.erase(id)
# remove from export dialog
Global.export_dialog.image_exports.erase(id)
Global.export_dialog.spritesheet_exports.erase(id)
return
@ -392,8 +420,8 @@ func file_format_description(format_enum: int) -> String:
return "APNG Image"
_:
# If a file format description is not found, try generating one
for key in FileFormat.keys():
if FileFormat[key] == format_enum:
for key in custom_file_formats.keys():
if custom_file_formats[key] == format_enum:
return str(key.capitalize())
return ""

View file

@ -476,57 +476,37 @@ class ExportAPI:
var ExportTab := Export.ExportTab
func add_export_option(
format_info: Dictionary, exporter_generator, tab := ExportTab.IMAGE, is_animated := true
format_info: Dictionary,
exporter_generator: Object,
tab := ExportTab.IMAGE,
is_animated := true
) -> int:
# separate enum name and file name
# Separate enum name and file name
var extension = ""
var format_name = ""
if format_info.has("extension"):
extension = format_info["extension"]
if format_info.has("description"):
format_name = format_info["description"].to_upper().replace(" ", "_")
# change format name if another one uses the same name
for i in range(Export.FileFormat.size()):
format_name = format_info["description"].strip_edges().to_upper().replace(" ", "_")
# Change format name if another one uses the same name
var existing_format_names = Export.FileFormat.keys() + Export.custom_file_formats.keys()
for i in range(existing_format_names.size()):
var test_name = format_name
if i != 0:
test_name = str(test_name, "_", i)
if !Export.FileFormat.keys().has(test_name):
if !existing_format_names.has(test_name):
format_name = test_name
break
# add to FileFormat enum
var id := Export.FileFormat.size()
for i in Export.FileFormat.size(): # use an empty id if it's available
if !Export.FileFormat.values().has(i):
id = i
# Export.FileFormat.merge({format_name: id})
# add exporter generator
Export.custom_exporter_generators.merge({id: [exporter_generator, extension]})
# add to animated (or not)
if is_animated:
Export.animated_formats.append(id)
# add to export dialog
match tab:
ExportTab.IMAGE:
Global.export_dialog.image_exports.append(id)
ExportTab.SPRITESHEET:
Global.export_dialog.spritesheet_exports.append(id)
_: # Both
Global.export_dialog.image_exports.append(id)
Global.export_dialog.spritesheet_exports.append(id)
# Setup complete, add the exporter
var id = Export.add_custom_file_format(
format_name, extension, exporter_generator, tab, is_animated
)
ExtensionsApi.add_action("add_exporter")
return id
func remove_export_option(id: int):
if Export.custom_exporter_generators.has(id):
# remove enum
Export.remove_file_format(id)
# remove exporter generator
Export.custom_exporter_generators.erase(id)
# remove from animated (or not)
Export.animated_formats.erase(id)
# add to export dialog
Global.export_dialog.image_exports.erase(id)
Global.export_dialog.spritesheet_exports.erase(id)
Export.remove_custom_file_format(id)
ExtensionsApi.remove_action("add_exporter")
@ -623,3 +603,12 @@ class SignalsAPI:
func disconnect_current_cel_texture_changed(callable: Callable):
texture_changed.disconnect(callable)
ExtensionsApi.remove_action("texture_changed")
# Export dialog signals
func connect_export_about_to_preview(target: Object, method: String):
Global.export_dialog.about_to_preview.connect(Callable(target, method))
ExtensionsApi.add_action("export_about_to_preview")
func disconnect_export_about_to_preview(target: Object, method: String):
Global.export_dialog.about_to_preview.disconnect(Callable(target, method))
ExtensionsApi.remove_action("export_about_to_preview")

View file

@ -166,7 +166,11 @@ func _add_extension(file_name: String) -> void:
"The extension %s will not work on this version of Pixelorama \n"
% file_name_no_ext
)
var required_text := "Requires API version: %s" % str(supported_api_versions)
var required_text := str(
"Extension works on API versions: %s" % str(supported_api_versions),
"\n",
"But Pixelorama's API version is: %s" % ExtensionsApi.get_api_version()
)
Global.error_dialog.set_text(str(err_text, required_text))
Global.error_dialog.popup_centered()
Global.dialog_open(true)

View file

@ -2,6 +2,7 @@ extends ConfirmationDialog
## Called when user resumes export after filename collision
signal resume_export_function
signal about_to_preview(Dictionary)
var preview_current_frame := 0
var preview_frames: Array[Texture2D] = []
@ -18,6 +19,9 @@ var spritesheet_exports: Array[Export.FileFormat] = [
Export.FileFormat.PNG, Export.FileFormat.WEBP, Export.FileFormat.JPEG
]
var _preview_images: Array[Image]
var _preview_durations: PackedFloat32Array
@onready var tabs: TabBar = $VBoxContainer/TabBar
@onready var checker: ColorRect = $"%TransparentChecker"
@onready var previews: GridContainer = $"%Previews"
@ -91,18 +95,27 @@ func show_tab() -> void:
func set_preview() -> void:
_preview_images = Export.processed_images.duplicate()
_preview_durations = Export.durations.duplicate()
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 Export.processed_images.size() == 1:
if _preview_images.size() == 1:
previews.columns = 1
add_image_preview(Export.processed_images[0])
add_image_preview(_preview_images[0])
else:
if Export.is_single_file_format():
previews.columns = 1
add_animated_preview()
else:
previews.columns = ceili(sqrt(Export.processed_images.size()))
for i in range(Export.processed_images.size()):
add_image_preview(Export.processed_images[i], i + 1)
previews.columns = ceili(sqrt(_preview_images.size()))
for i in range(_preview_images.size()):
add_image_preview(_preview_images[i], i + 1)
if Global.current_project.file_format == Export.FileFormat.GIF:
$"%GifWarning".visible = true
@ -133,7 +146,7 @@ func add_animated_preview() -> void:
preview_current_frame = 0
preview_frames = []
for processed_image in Export.processed_images:
for processed_image in _preview_images:
var texture := ImageTexture.create_from_image(processed_image)
preview_frames.push_back(texture)
@ -146,7 +159,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 = Export.durations[preview_current_frame]
frame_timer.wait_time = _preview_durations[preview_current_frame]
frame_timer.start()
@ -226,8 +239,8 @@ func create_layer_list() -> void:
func update_dimensions_label() -> void:
if Export.processed_images.size() > 0:
var new_size: Vector2 = Export.processed_images[0].get_size() * (Export.resize / 100.0)
if _preview_images.size() > 0:
var new_size: Vector2 = _preview_images[0].get_size() * (Export.resize / 100.0)
dimension_label.text = str(new_size.x, "×", new_size.y)
@ -390,7 +403,7 @@ func _on_FrameTimer_timeout() -> void:
else:
preview_current_frame += 1
frame_timer.wait_time = Export.durations[preview_current_frame - 1]
frame_timer.wait_time = _preview_durations[preview_current_frame - 1]
frame_timer.start()

View file

@ -8,6 +8,7 @@
[node name="ExportDialog" type="ConfirmationDialog"]
canvas_item_default_texture_filter = 0
title = "Export..."
position = Vector2i(0, 36)
size = Vector2i(700, 600)
exclusive = false
dialog_hide_on_ok = false
@ -17,8 +18,8 @@ script = ExtResource("1")
custom_minimum_size = Vector2(330, 0)
offset_left = 8.0
offset_top = 8.0
offset_right = 524.0
offset_bottom = 494.0
offset_right = 692.0
offset_bottom = 551.0
size_flags_vertical = 3
[node name="TabBar" type="TabBar" parent="VBoxContainer"]
@ -300,8 +301,8 @@ layout_mode = 3
anchors_preset = 0
offset_left = 8.0
offset_top = 8.0
offset_right = 524.0
offset_bottom = 494.0
offset_right = 692.0
offset_bottom = 551.0
mouse_filter = 2
[node name="PathDialog" type="FileDialog" parent="Popups"]