├── icon.png ├── .gitignore ├── default_env.tres ├── addons └── HTML5FileExchange │ ├── plugin.gd │ ├── plugin.cfg │ └── HTML5FileExchange.gd ├── README.md ├── Sample.gd ├── project.godot ├── icon.png.import ├── LICENSE └── sample.tscn /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pukkah/HTML5-File-Exchange-for-Godot/HEAD/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | data_*/ 10 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /addons/HTML5FileExchange/plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | 5 | func _enter_tree(): 6 | add_autoload_singleton("HTML5File", "res://addons/HTML5FileExchange/HTML5FileExchange.gd") 7 | 8 | 9 | func _exit_tree(): 10 | remove_autoload_singleton("HTML5File") 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTML5 File Exchange for Godot 3.4 2 | 3 | ## Usage 4 | 5 | Check sample.tscn as a upload & download example. 6 | 7 | ### Without sample 8 | 9 | * Copy addons to your project. 10 | * Enable the plugin. (A `HTML5File` singleton should be added into autoload list). 11 | * If the HTML5 debugging option is not enabled, add a HTML5 export item. -------------------------------------------------------------------------------- /addons/HTML5FileExchange/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="HTML5 File Exchange" 4 | description="Open/Import and Save/Export files in HTML5 environment. 5 | Supported input - *.png, *.jpeg, *.webp 6 | Supported output - *.png 7 | 8 | Usage: 9 | Get file from User - 10 | var image:Image = yield(HTML5File.get_image(), \"completed\") 11 | Send file to User - 12 | HTML5File.save_image(image:Image, fileName:String = \"export\")" 13 | author="laame" 14 | version="0.1.1" 15 | script="plugin.gd" 16 | -------------------------------------------------------------------------------- /Sample.gd: -------------------------------------------------------------------------------- 1 | extends TextureRect 2 | 3 | # Called when the node enters the scene tree for the first time. 4 | func _ready(): 5 | pass # Replace with function body. 6 | 7 | func _on_UploadButton_pressed(): 8 | HTML5File.load_image() 9 | 10 | var image = yield(HTML5File, "load_completed") 11 | 12 | var tex = ImageTexture.new() 13 | tex.create_from_image(image, 0) 14 | 15 | texture = tex; 16 | 17 | func _on_DownloadButton_pressed(): 18 | var image = texture.get_data() 19 | HTML5File.save_image(image, "image.png") 20 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | [application] 12 | 13 | config/name="HTML5-File-Exchange-for-Godot" 14 | run/main_scene="res://sample.tscn" 15 | config/icon="res://icon.png" 16 | 17 | [autoload] 18 | 19 | HTML5File="*res://addons/HTML5FileExchange/HTML5FileExchange.gd" 20 | 21 | [editor_plugins] 22 | 23 | enabled=PoolStringArray( "res://addons/HTML5FileExchange/plugin.cfg" ) 24 | 25 | [physics] 26 | 27 | common/enable_pause_aware_picking=true 28 | 29 | [rendering] 30 | 31 | environment/default_environment="res://default_env.tres" 32 | -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 laame 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sample.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://Sample.gd" type="Script" id=1] 4 | 5 | [node name="Control" type="Control"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | 9 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 10 | margin_right = 40.0 11 | margin_bottom = 40.0 12 | 13 | [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] 14 | margin_right = 136.0 15 | margin_bottom = 20.0 16 | 17 | [node name="UploadButton" type="Button" parent="VBoxContainer/HBoxContainer"] 18 | margin_right = 57.0 19 | margin_bottom = 20.0 20 | text = "Upload" 21 | 22 | [node name="DownloadButton" type="Button" parent="VBoxContainer/HBoxContainer"] 23 | margin_left = 61.0 24 | margin_right = 136.0 25 | margin_bottom = 20.0 26 | text = "Download" 27 | 28 | [node name="TextureRect" type="TextureRect" parent="VBoxContainer"] 29 | margin_top = 24.0 30 | margin_right = 136.0 31 | margin_bottom = 24.0 32 | script = ExtResource( 1 ) 33 | 34 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/UploadButton" to="VBoxContainer/TextureRect" method="_on_UploadButton_pressed"] 35 | [connection signal="pressed" from="VBoxContainer/HBoxContainer/DownloadButton" to="VBoxContainer/TextureRect" method="_on_DownloadButton_pressed"] 36 | -------------------------------------------------------------------------------- /addons/HTML5FileExchange/HTML5FileExchange.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | signal read_completed 4 | signal load_completed(image) 5 | 6 | var js_callback = JavaScript.create_callback(self, "load_handler"); 7 | var js_interface; 8 | 9 | func _ready(): 10 | if OS.get_name() == "HTML5" and OS.has_feature('JavaScript'): 11 | _define_js() 12 | js_interface = JavaScript.get_interface("_HTML5FileExchange"); 13 | 14 | func _define_js()->void: 15 | #Define JS script 16 | JavaScript.eval(""" 17 | var _HTML5FileExchange = {}; 18 | _HTML5FileExchange.upload = function(gd_callback) { 19 | canceled = true; 20 | var input = document.createElement('INPUT'); 21 | input.setAttribute("type", "file"); 22 | input.setAttribute("accept", "image/png, image/jpeg, image/webp"); 23 | input.click(); 24 | input.addEventListener('change', event => { 25 | if (event.target.files.length > 0){ 26 | canceled = false;} 27 | var file = event.target.files[0]; 28 | var reader = new FileReader(); 29 | this.fileType = file.type; 30 | // var fileName = file.name; 31 | reader.readAsArrayBuffer(file); 32 | reader.onloadend = (evt) => { // Since here's it's arrow function, "this" still refers to _HTML5FileExchange 33 | if (evt.target.readyState == FileReader.DONE) { 34 | this.result = evt.target.result; 35 | gd_callback(); // It's hard to retrieve value from callback argument, so it's just for notification 36 | } 37 | } 38 | }); 39 | } 40 | """, true) 41 | 42 | func load_handler(_args): 43 | emit_signal("read_completed") 44 | 45 | func load_image(): 46 | if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): 47 | return 48 | 49 | js_interface.upload(js_callback); 50 | 51 | yield(self, "read_completed") 52 | 53 | var imageType = js_interface.fileType; 54 | var imageData = JavaScript.eval("_HTML5FileExchange.result", true) # interface doesn't work as expected for some reason 55 | 56 | var image = Image.new() 57 | var image_error 58 | match imageType: 59 | "image/png": 60 | image_error = image.load_png_from_buffer(imageData) 61 | "image/jpeg": 62 | image_error = image.load_jpg_from_buffer(imageData) 63 | "image/webp": 64 | image_error = image.load_webp_from_buffer(imageData) 65 | var invalidType: 66 | print("Unsupported file format - %s." % invalidType) 67 | return 68 | 69 | if image_error: 70 | print("An error occurred while trying to display the image.") 71 | 72 | emit_signal("load_completed", image) 73 | 74 | func save_image(image:Image, fileName:String = "export.png")->void: 75 | if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): 76 | return 77 | 78 | image.clear_mipmaps() 79 | var buffer = image.save_png_to_buffer() 80 | JavaScript.download_buffer(buffer, fileName) 81 | --------------------------------------------------------------------------------