From fb7c1a18741c6eb1056eda1facc2100ffcd748ac Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Tue, 30 Jun 2020 18:32:16 +0300 Subject: [PATCH] Saving .png files is now possible in HTML5 Addresses #280. Huge thanks to https://github.com/Pukkah/HTML5-File-Exchange-for-Godot --- project.godot | 1 + src/Autoload/HTML5FileExchange.gd | 105 ++++++++++++++++++++++++++++++ src/UI/Dialogs/ExportDialog.gd | 14 +++- 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 src/Autoload/HTML5FileExchange.gd diff --git a/project.godot b/project.godot index 68df72cd7..8c4a5739f 100644 --- a/project.godot +++ b/project.godot @@ -97,6 +97,7 @@ Global="*res://src/Autoload/Global.gd" Import="*res://src/Autoload/Import.gd" OpenSave="*res://src/Autoload/OpenSave.gd" DrawingAlgos="*res://src/Autoload/DrawingAlgos.gd" +Html5FileExchange="*res://src/Autoload/HTML5FileExchange.gd" [debug] diff --git a/src/Autoload/HTML5FileExchange.gd b/src/Autoload/HTML5FileExchange.gd new file mode 100644 index 000000000..9027be7ef --- /dev/null +++ b/src/Autoload/HTML5FileExchange.gd @@ -0,0 +1,105 @@ +extends Node +# Code taken and modified from https://github.com/Pukkah/HTML5-File-Exchange-for-Godot +# Thanks to Pukkah from GitHub for providing the original code + +signal InFocus + + +func _ready() -> void: + if OS.get_name() == "HTML5" and OS.has_feature('JavaScript'): + _define_js() + + +func _notification(notification:int) -> void: + if notification == MainLoop.NOTIFICATION_WM_FOCUS_IN: + emit_signal("InFocus") + +func _define_js() -> void: + # Define JS script + JavaScript.eval(""" + var fileData; + var fileType; + var fileName; + var canceled; + function upload_image() { + canceled = true; + var input = document.createElement('INPUT'); + input.setAttribute("type", "file"); + input.setAttribute("accept", "image/png, image/jpeg, image/webp"); + input.click(); + input.addEventListener('change', event => { + if (event.target.files.length > 0){ + canceled = false;} + var file = event.target.files[0]; + var reader = new FileReader(); + fileType = file.type; + fileName = file.name; + reader.readAsArrayBuffer(file); + reader.onloadend = function (evt) { + if (evt.target.readyState == FileReader.DONE) { + fileData = evt.target.result; + } + } + }); + } + function download(fileName, byte, type) { + var buffer = Uint8Array.from(byte); + var blob = new Blob([buffer], { type: type}); + var link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + link.download = fileName; + link.click(); + }; + """, true) + + +func load_image() -> Image: + if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): + return + + # Execute js function + JavaScript.eval("upload_image();", true) # opens prompt for choosing file + + yield(self, "InFocus") # wait until js prompt is closed + + yield(get_tree().create_timer(0.1), "timeout") #give some time for async js data load + + if JavaScript.eval("canceled;", true): # if File Dialog closed w/o file + return + + # use data from png data + var imageData + while true: + imageData = JavaScript.eval("fileData;", true) + if imageData != null: + break + yield(get_tree().create_timer(1.0), "timeout") # need more time to load data + + var imageType = JavaScript.eval("fileType;", true) + var imageName = JavaScript.eval("fileName;", true) + + var image = Image.new() + var image_error + match imageType: + "image/png": + image_error = image.load_png_from_buffer(imageData) + "image/jpeg": + image_error = image.load_jpg_from_buffer(imageData) + "image/webp": + image_error = image.load_webp_from_buffer(imageData) + var invalid_type: + print(invalid_type) + return + if image_error: + print("An error occurred while trying to display the image.") + return + else: + return image + + +func save_image(image : Image, file_name : String = "export") -> void: + if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): + return + + var png_data = Array(image.get_data()) + JavaScript.eval("download('%s', %s, 'image/png');" % [file_name, str(png_data)], true) diff --git a/src/UI/Dialogs/ExportDialog.gd b/src/UI/Dialogs/ExportDialog.gd index c3536fd7c..333b196de 100644 --- a/src/UI/Dialogs/ExportDialog.gd +++ b/src/UI/Dialogs/ExportDialog.gd @@ -77,6 +77,11 @@ func _ready() -> void: $VBoxContainer/AnimationOptions/AnimationType.selected = AnimationType.MULTIPLE_FILES $VBoxContainer/AnimationOptions/AnimationType.disabled = true + # If we're on HTML5, don't let the user change the directory path + if OS.get_name() == "HTML5": + $VBoxContainer/Path.visible = false + directory_path = "user://" + func show_tab() -> void: $VBoxContainer/FrameOptions.hide() @@ -348,9 +353,12 @@ func export_processed_images(ignore_overwrites : bool) -> void: $GifExporter.end_export() else: 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") + if OS.get_name() == "HTML5": + Html5FileExchange.save_image(processed_images[i], export_paths[i].get_file()) + else: + 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