Hello Mikael H.
It is very much possible to load an image (or any file) from the user hard drive into your WebGL application.
You can not do this directly from your application, but you can implement a JavaScript plugin that will do the job. Normally I would suggest to transfer data between JavaScript and WebGL using module heap, but for people not familiar with the asm.js infrastructure it might be more convenient to use blobs. The whole process can be split into two separate tasks:
1) Uploading an image using html button
First, you need to implement a JavaScript plugin that will handle the browser file dialog, create a blob for the selected file and transfer the blob address to the WebGL application. You can create the following Assets/Plugins/ImageUploader.jslib file for that:
var ImageUploaderPlugin = {
ImageUploaderInit: function() {
var fileInput = document.createElement('input');
fileInput.setAttribute('type', 'file');
fileInput.onclick = function (event) {
this.value = null;
};
fileInput.onchange = function (event) {
SendMessage('Canvas', 'FileSelected', URL.createObjectURL(event.target.files[0]));
}
document.body.appendChild(fileInput);
}
};
mergeInto(LibraryManager.library, ImageUploaderPlugin);
Now you can attach the following script to your Canvas, that will initialize the plugin and load the texture from the generated blob address:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class CanvasScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void ImageUploaderInit();
IEnumerator LoadTexture (string url) {
WWW image = new WWW (url);
yield return image;
Texture2D texture = new Texture2D (1, 1);
image.LoadImageIntoTexture (texture);
Debug.Log ("Loaded image size: " + texture.width + "x" + texture.height);
}
void FileSelected (string url) {
StartCoroutine(LoadTexture (url));
}
void Start () {
ImageUploaderInit ();
}
}
2) Uploading an image using UI button
This one is quite tricky. The idea would be of course to make the html browse button hidden, and simulate its click programmatically. We are however facing a security restriction here, specifically, it is not possible to launch the onclick event handler programmatically, unless you make the click() call from another event handler initiated by the user. And as in most complex systems, in Unity WebGL input events are not processed directly but instead are passed through an intermediate queue. This means that the initial JavaScript event handler initiated by the user has been already processed by the time when you receive the OnClick event in the managed code.
Nevertheless, there is a trick that we can perform to make it work: as soon as the user pushes the UI button down, we can register onclick JavaScript event for the whole WebGL canvas, so that when the user releases the button, we will get a user-initiated onclick JavaScript event, and perform the input click simulation in the handler.
You will need the following Assets/Plugins/ImageUploader.jslib plugin:
var ImageUploaderPlugin = {
ImageUploaderCaptureClick: function() {
if (!document.getElementById('ImageUploaderInput')) {
var fileInput = document.createElement('input');
fileInput.setAttribute('type', 'file');
fileInput.setAttribute('id', 'ImageUploaderInput');
fileInput.style.visibility = 'hidden';
fileInput.onclick = function (event) {
this.value = null;
};
fileInput.onchange = function (event) {
SendMessage('Canvas', 'FileSelected', URL.createObjectURL(event.target.files[0]));
}
document.body.appendChild(fileInput);
}
var OpenFileDialog = function() {
document.getElementById('ImageUploaderInput').click();
document.getElementById('canvas').removeEventListener('click', OpenFileDialog);
};
document.getElementById('canvas').addEventListener('click', OpenFileDialog, false);
}
};
mergeInto(LibraryManager.library, ImageUploaderPlugin);
And the following script to interact with it:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class CanvasScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void ImageUploaderCaptureClick();
IEnumerator LoadTexture (string url) {
WWW image = new WWW (url);
yield return image;
Texture2D texture = new Texture2D (1, 1);
image.LoadImageIntoTexture (texture);
Debug.Log ("Loaded image size: " + texture.width + "x" + texture.height);
}
void FileSelected (string url) {
StartCoroutine(LoadTexture (url));
}
public void OnButtonPointerDown () {
ImageUploaderCaptureClick ();
}
}
The OnButtonPointerDown should be triggered by the Pointer Down event (available through the Event Trigger component, that you can attach to your UI button)
The second method is of course not 100% reliable (for example, user can push the button, move the mouse outside the canvas, then release the button, so that the attached onclick event handler remains unprocessed), but for most use cases it should be quite appropriate.