The ImageTexture I'm creating comes out solid white

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

I’m overriding the Node2D.draw() method so that I can draw some custom images in my node. However, my attempt to build an image from pixels is failing. I am setting the pixels of my image to different colors, but when I call the draw_texture() method, it’s as if my image contains only white. What am I doing wrong?

func _draw():
	var bg = Image.new();
	bg.create(100, 100, false, Image.FORMAT_RGBA8);
	bg.lock();
	
	for j in range(100):
		for i in range(100):
			bg.set_pixel(i, j, Color(
				float(i) / 100.0,
				float(j) / 100.0,
				0.0,
				1.0
			));
	
	bg.unlock();
	bg.generate_mipmaps();
	
	var bgTex = ImageTexture.new();
	bgTex.create_from_image(bg);
	
	draw_texture(bgTex, Vector2(0.0, 0.0))

Looks like this fails if I try and do it in the _draw() callback. However, if I use a Sprite and build the ImageTexture in _ready() or _process(delta) and assign the result to my Icon’s texture variable, it displays properly.

kitfox | 2020-07-28 01:53

:bust_in_silhouette: Reply From: ChristianSF

Aren’t you using print() or printt() to output your variables’ contents to the debugging window? There you could display each created pixel’s color and check whether the generated values are as expected.

I was checking for that in the debugger window. The Image was being built correctly- it was creating the ImageTexture that was failing. I’m guessing the _draw() method is called on a different thread which prevents the ImageTexture from being built.

kitfox | 2020-07-28 08:09

:bust_in_silhouette: Reply From: avencherus

For performance reasons the execution time of CanvasItem rendering is not done at the same time as other things in script. This isn’t documented very well, but it is also the reason why you can only use the draw functions in the _draw() function or when signaled/notified.

If I’m remembering correctly, what happens in your code is that the draw_texture is going to take place slightly after this function. So the GDScript function finishes first and the scoped variables are cleared off the stack and the contents are no longer available.

It’s not very intuitive, and not ideal, but you can work around it in the way you mentioned, but more specifically by lifting the variable you’re using to a higher scope. Then it will be alive and well for the visual server’s reference later.

IE - As seen like the first line I added to your code below:

var bgTex : ImageTexture # <--- Lift var to this scope.

func _draw():

	var bg = Image.new();
	bg.create(100, 100, false, Image.FORMAT_RGBA8);
	bg.lock();

	for j in range(100):
		for i in range(100):
			bg.set_pixel(i, j, Color(
				float(i) / 100.0,
				float(j) / 100.0,
				0.0,
				1.0
			));

	bg.unlock();
	bg.generate_mipmaps();

	bgTex = ImageTexture.new();
	bgTex.create_from_image(bg);

	draw_texture(bgTex, Vector2(0.0, 0.0))