Unable to Load an Image as Data Using ImageLoader

I am trying to load images as data so that I can modify one with the other and then display the result as a texture. However, I am stuck on the first part and have had no success even getting one image to load.

Following the many examples online, I have tried the following sequence of commands:

let geometry, material, texture, mesh, color;
let doData = 0;		// Address of destination image data
let imagloader = new ImageLoader();
let PlnObj = new Object3D();
scene.add(PlnObj);

imagloader.load('https://threejs.org/examples/textures/crate.gif', function(image) {
	let canvas = document.createElement('canvas');
	canvas.width = 512;
	canvas.height = 512;
	let context = canvas.getContext('2d');
	context.drawImage(image,0,0);
	let imageData = context.getImageData(0,0,512,512);
	d0Data = imageData.data;		// save for later;
});

To see if the image has loaded, I use these commands to display the data:

texture = new DataTexture(d0Data.data, d0Size, d0Size, RGBAFormat);
material = new MeshPhongMaterial({map: texture});
mesh = new Mesh(geometry, material);
PlnObj.add(mesh);

I don’t get any error messages, but all that is displayed is a dark blue plane.

Just to check I tried:

let txtrloader = new TextureLoader();
texture = txtrloader.load('https://threejs.org/examples/textures/crate.gif.jpg');
etc.

and the image displays just fine.

I looked at various examples on JSfiddle and none of them appeared to work either.

Then I remembered that I had read that the ImageLoader does not have support for progress events. So is the whole “function (image)” portion non operative? Is the problem that I am displaying things before they have been loaded?

I have used both three.js and the three.js module and the results are the same.

When are you creating the texture? The texture needs to be created in the callback function you’re providing to the image loader, since you need the data to be created before it.

In this kind of scenario if you can provide the full code or a codepen / jsfiddle it’ll be more useful than posting snippets, since here we can;t determine when you’re creating the texture.

BTW as a side note I would simply use a CanvasTexture if you’re going to be modifying your image through the canvas element.

1 Like

I tried creating the texture both inside and outside of the callback function. If inside the function, I got a white screen. If outside, I got a dark blue screen. This suggests to me that it is displaying the texture before it is done loading.

Here is a fiddle that is supposed to be doing exactly what I am trying to do, but it doesn’t appear to be working either.

Regarding CanvasTexture: What I am trying to do is to: (1) load a blank destination image (512X512) and several source images (256X256) ad data files (what I am trying to do here); (2) select and copy selected source images into the destination image in a 3X3 grid (I have this step done, but using just color data), and (3) take the final destination image and convert it to a texture (with mip maps and all) and display it.

So, I am currently trying to solve the first part of the problem and haven’t tackled the last part yet. Would CanvasTexture be the best solution for all 3 of these tasks?

Add: texture.needsUpdate = !0;

Okay I added that and put everything inside the loading routine:

imagloader.load(‘https://threejs.org/examples/textures/crate.gif’, function(image) {
let canvas = document.createElement(‘canvas’);
canvas.width = 512;
canvas.height = 512;
let context = canvas.getContext(‘2d’);
context.drawImage(image,0,0);
let imageData = context.getImageData(0,0,512,512);
d0Data = imageData.data;
texture = new DataTexture(d0Data.data, d0Size, d0Size, RGBAFormat);
material = new MeshPhongMaterial({map: texture});
texture.needsUpdate = !0;
});

Still a white screen.

When I had the material command outside of the routine, it was giving me an error message “map parameter not defined”, which indicates that the texture and imageData were blank.

I will try to create a fiddle of my own to see if we can flush out the problem.

EDIT
Here is my fiddle.
If you either of the first two variations (by removing the comments), you will see that they work okay.
(I will change this so the versions display side by side.)

  • Image is 256x256
  • d0Size is what exactly.

Good advice.

Here is the jsfiddle without white screen
My_code (1).html (3.4 KB)

doSize = 512, the size of the destination image, which is 512X512.
I am trying to load Crate.gif, which is a 256X256 image, but it displayed okay.
I am switching to Disturb.jpg which is a 512X512 image to eliminate that variable.
Displays okay with TextureLoader, but white screen with ImageLoader.

Here is the latest fiddle with them side by side.

  1. Left is texture generated using a random color.
  2. Center is texture loaded with TextureLoader. It is the 256X256 crate image.
  3. Right is the texture loaded with ImageLoader.

To avoid cross-contamination, I gave them all local names: texture1, 2 and 3. Also I am loading a different image in #3 - the 512X512 disturbed image.

However, as you can see, there still appears to be some cross-contamination because #3 is picking up whatever image was loaded into #2.

Okay, I found a way to display the texture, but I will have to confirm that it is making the data available for my manipulation. Here is what I did:

In the HTML section, I added:
<canvas hidden id="canvas" width=512 height=512></canvas>

In the variables section, I put this:

let canvas = document.getElementById("canvas");
let	context = canvas.getContext('2d');
context.fillStyle = "black"
context.fillRect(0,0,canvas.width,canvas.height);
let d0Imag = new Image();
d0Imag.crossOrigin = "Anonymous";    //prevents message about contamination

In the program, I added:

d0Imag.onload = function() {
	context.drawImage(d0Imag,0,0);
	d0Data = context.getImageData(0,0,512,512);
	texture = new DataTexture(d0Data, d0Size, d0Size, RGBAFormat);
	texture.needsUpdate = true;
	material = new MeshPhongMaterial({map: texture});
	geometry = new PlaneGeometry(512, 512);
	mesh = new Mesh(geometry, material);
	Pln3Obj.add(mesh);
};
d0Imag.src = 'https://threejs.org/examples/textures/disturb.jpg';

I will post this to a jsfiddle, to verify that it works.

Then I will have to determine how to add source images (can I re-use the canvas for smaller images?) and confirm that I can copy their data to the data to the destination.

Here is the jsfiddle.

My_code.html (5.5 KB)

1 Like

Excellent! Now I just need to create a routine to load all the textures (starting with just 2) and not exiting until the “files loaded” equals the required number. Then I “should” be able to modify and display the modified destination file.

EDIT

And I immediately ran into problems: For some reason, the imageData.data is not being saved.

I used the same 3 object display to illustrate the problem, as shown in this fiddle.

As before, the left object shows destination texture (512X512) which is going to be overwritten. The middle object shows the source texture. As before, I am using the crate.gif which is 256X256. The s0Area is 256X256. This is where I extracted the imageData.dat.

To be safe I copied the imageData.dat into a separate array, which I defined as follows:

let stData = new Uint8Array(4*s0Area);

and then copied the imageData.dat into that array:

for (let i = 0; i < 4*s0Area; i ++) {
	stData[i] = imageData.data[i];
}

To confirm that the transfer had worked, I used the stData to create texture2 which is correctly displayed in the middle object.

 texture2 = new THREE.DataTexture(stData, s0Size, s0Size, THREE.RGBAFormat);

I then tried copying data from the stData into the destination data. It worked (as shown in the left object), but the results were a black area. I then tested this out on the right object by using stData to create the same texture that I had created for the middle object. But, instead of a crate, the result was a black texture. So something is apparently happening to the cause the stData to disappear or turn to black.

I would normally expect that I am doing something wrong with arrays. But, if so, it shouldn’t have correctly created texture2. Very puzzling.

ADDENDA
I have not found the answer. But it may not be an issue if I can use the canvas as the place to assemble my textures into a single texture - rather than the source texture. That way I would only need to getImageData once. Working on it.

I have decided to try to assemble the final image in the canvas, picking and choosing which pre-loaded images belong in the 9 locations. From preliminary testing where I inserted the same texture in the 9 locations, the CanvasTexture appears to be working perfectly. So thanks for bringing this to my attention.