How to load an image and set it as texture in HTML5 export?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By arun_mani_j

Hi

in my game, user can choose an image from their file system and it should be set as texture for a node. How can I do this?

Currently, I have an <input id="myFileInp" type="file"> in my HTML. I don’t know what to do after user clicks the input and selects the image.

There is FileReader interface in JavaScript to read the file blob. So I should be able to copy the image by reading it and writing it to a file in user://image.ext. Since it is HTML5, the file gets stored in IndexedDB. Then I can use Image.load_from_file(user://image.ext) to load the image.

But the issue I’m facing is in the reading and writing of the image. MDN says I can read the image as an ArrayBuffer to get the raw data. To read this data, I should use DataView or TypedArray. This is what confuses me… what kind of array should I use to read it? int8 or uint8 or what else? Or does it depend upon the image or I can use any type as long as I read and write them in same way (say using Godot’s PackedByteArray)?

Or am I complicating it? Is there any simple way that lets Godot handle all the image format-specific properties? FileReader also has a method to get a data URL. Can Godot read images from it?

Thanks for reading!

:bust_in_silhouette: Reply From: jgee_23_

In order to load an image from a path to a node, the node must be a Sprite2D if in Godot4 or Sprite if in Godot3.

Via code, you get the Sprite and set its texture via $Sprite.texture = img, where var img = load(path).

I’m not quite familiar with the ArrayBuffer stuff, but as long as you have the path to the file you should be able to set is as a texture…

The issue is indeed in getting the file. That is once the user uploads an image, how do I get it into the Godot’s area?

arun_mani_j | 2023-05-08 13:05

:bust_in_silhouette: Reply From: arun_mani_j

This is my current approach. Though it solves the issue, I feel like it can be improved.

var reader = JavaScriptBridge.create_object("FileReader")
var _set_picture = JavaScriptBridge.create_callback(set_picture)
func set_picture(_args):
    if pictureInp.files.length == 0:
	    return

    reader.readAsArrayBuffer(pictureInp.files[0])
    reader.addEventListener("load", _save_set_picture)
var _save_set_picture = JavaScriptBridge.create_callback(save_set_picture)
func save_set_picture(args):
    var srcArray = JavaScriptBridge.create_object("Uint8Array", reader.result)
    var buffer = PackedByteArray()

    for i in range(srcArray.length):
	    buffer.append(srcArray[i])

    var path = "user://" + pictureInp.files[0].name
    var file = FileAccess.open(path, FileAccess.WRITE)
    file.store_buffer(buffer)
    var image = Image.load_from_file(path)
        mynode.texture.set_image(image)