diff --git a/README.md b/README.md index bd3ea14..bdd2590 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ # HTML5 File Exchange for Godot 3.2 + +## Usage + +* Copy addons to your project. +* Enable the plugin. (A `HTML5File` singleton should be added into autoload list). +* Add a HTML5 export item to enable HTML5 debugging option. +* Check sample.tscn for usage. \ No newline at end of file diff --git a/addons/HTML5FileExchange/HTML5FileExchange.gd b/addons/HTML5FileExchange/HTML5FileExchange.gd index 3d7ba80..637356c 100644 --- a/addons/HTML5FileExchange/HTML5FileExchange.gd +++ b/addons/HTML5FileExchange/HTML5FileExchange.gd @@ -1,24 +1,21 @@ extends Node -signal InFocus +signal read_completed +signal load_completed(image) + +var js_callback = JavaScript.create_callback(self, "load_handler"); +var js_interface; func _ready(): 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") + js_interface = JavaScript.get_interface("_HTML5FileExchange"); func _define_js()->void: #Define JS script JavaScript.eval(""" - var fileData; - var fileType; - var fileName; - var canceled; - function upload() { + var _HTML5FileExchange = {}; + _HTML5FileExchange.upload = function(gd_callback) { canceled = true; var input = document.createElement('INPUT'); input.setAttribute("type", "file"); @@ -29,56 +26,32 @@ func _define_js()->void: canceled = false;} var file = event.target.files[0]; var reader = new FileReader(); - fileType = file.type; - fileName = file.name; + this.fileType = file.type; + // var fileName = file.name; reader.readAsArrayBuffer(file); - reader.onloadend = function (evt) { + reader.onloadend = (evt) => { // Since here's it's arrow function, "this" still refers to _HTML5FileExchange if (evt.target.readyState == FileReader.DONE) { - fileData = evt.target.result; + this.result = evt.target.result; + gd_callback(); // It's hard to retrieve value from callback argument, so it's just for notification } } }); } - function download(fileName, byte) { - var buffer = Uint8Array.from(byte); - var blob = new Blob([buffer], { type: 'image/png'}); - var link = document.createElement('a'); - link.href = window.URL.createObjectURL(blob); - link.download = fileName; - link.click(); - }; """, true) + +func load_handler(_args): + emit_signal("read_completed") - -func load_image()->Image: +func load_image(): if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): return - - #Execute js function - JavaScript.eval("upload();", true) #opens promt for choosing file + + js_interface.upload(js_callback); + + yield(self, "read_completed") - #label.text = "Wait for focus" - yield(self, "InFocus") #wait until js promt is closed - - #label.text = "Timer on for loading" - 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 - #label.text = "Canceled prompt" - return - - # use data from png data - #label.text = "Load image" - var imageData - while true: - imageData = JavaScript.eval("fileData;", true) - if imageData != null: - break - #label.text = "No image yet" - 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 imageType = js_interface.fileType; + var imageData = JavaScript.eval("_HTML5FileExchange.result", true) # interface doesn't work as expected for some reason var image = Image.new() var image_error @@ -90,39 +63,18 @@ func load_image()->Image: "image/webp": image_error = image.load_webp_from_buffer(imageData) var invalidType: - #label.text = "Unsupported file format - %s." % invalidType + print("Unsupported file format - %s." % invalidType) return + if image_error: - #label.text = "An error occurred while trying to display the image." - return - else: - return image - # Display texture - var tex = ImageTexture.new() - tex.create_from_image(image, 0) # Flag = 0 or else export is fucked! - Sprite.texture = tex - #loadedImage = image # Keep Image for later, just in case... - #loadedImageName = imageName - #label.text = "Image %s loaded as %s." % [imageName, imageType] - return - #label.text = "Something went wrong" + print("An error occurred while trying to display the image.") + + emit_signal("load_completed", image) - -func save_image(image:Image, fileName:String = "export")->void: +func save_image(image:Image, fileName:String = "export.png")->void: if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): return - + image.clear_mipmaps() - if image.save_png("user://export_temp.png"): - #label.text = "Error saving temp file" - return - var file:File = File.new() - if file.open("user://export_temp.png", File.READ): - #label.text = "Error opening file" - return - var pngData = Array(file.get_buffer(file.get_len())) #read data as PoolByteArray and convert it to Array for JS - file.close() - var dir = Directory.new() - dir.remove("user://export_temp.png") - JavaScript.eval("download('%s', %s);" % [fileName, str(pngData)], true) - #label.text = "Saving DONE" + var buffer = image.save_png_to_buffer() + JavaScript.download_buffer(buffer, fileName) diff --git a/addons/HTML5FileExchange/Sample.gd b/addons/HTML5FileExchange/Sample.gd new file mode 100644 index 0000000..c0f6753 --- /dev/null +++ b/addons/HTML5FileExchange/Sample.gd @@ -0,0 +1,19 @@ +extends TextureRect + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + +func _on_UploadButton_pressed(): + HTML5File.load_image() + + var image = yield(HTML5File, "load_completed") + + var tex = ImageTexture.new() + tex.create_from_image(image, 0) + + texture = tex; + +func _on_DownloadButton_pressed(): + var image = texture.get_data() + HTML5File.save_image(image, "image.png") diff --git a/addons/HTML5FileExchange/sample.tscn b/addons/HTML5FileExchange/sample.tscn new file mode 100644 index 0000000..b3c389a --- /dev/null +++ b/addons/HTML5FileExchange/sample.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/HTML5FileExchange/Sample.gd" type="Script" id=1] + +[node name="Control" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +margin_right = 40.0 +margin_bottom = 40.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +margin_right = 136.0 +margin_bottom = 20.0 + +[node name="UploadButton" type="Button" parent="VBoxContainer/HBoxContainer"] +margin_right = 57.0 +margin_bottom = 20.0 +text = "Upload" + +[node name="DownloadButton" type="Button" parent="VBoxContainer/HBoxContainer"] +margin_left = 61.0 +margin_right = 136.0 +margin_bottom = 20.0 +text = "Download" + +[node name="TextureRect" type="TextureRect" parent="VBoxContainer"] +margin_top = 24.0 +margin_right = 136.0 +margin_bottom = 24.0 +script = ExtResource( 1 ) + +[connection signal="pressed" from="VBoxContainer/HBoxContainer/UploadButton" to="VBoxContainer/TextureRect" method="_on_UploadButton_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/DownloadButton" to="VBoxContainer/TextureRect" method="_on_DownloadButton_pressed"] diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..20207a4 --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..c98fbb6 Binary files /dev/null and b/icon.png differ diff --git a/icon.png.import b/icon.png.import new file mode 100644 index 0000000..a4c02e6 --- /dev/null +++ b/icon.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..251c54a --- /dev/null +++ b/project.godot @@ -0,0 +1,31 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +[application] + +config/name="HTML5-File-Exchange-for-Godot" +run/main_scene="res://addons/HTML5FileExchange/sample.tscn" +config/icon="res://icon.png" + +[autoload] + +HTML5File="*res://addons/HTML5FileExchange/HTML5FileExchange.gd" + +[editor_plugins] + +enabled=PoolStringArray( "res://addons/HTML5FileExchange/plugin.cfg" ) + +[physics] + +common/enable_pause_aware_picking=true + +[rendering] + +environment/default_environment="res://default_env.tres"