image.get_pixel() always returns zero

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By monotonehell
:warning: Old Version Published before Godot 3 was released.

Hello! My first time playing with Godot & GDScript, so everything is new to me. I’m trying to get colour pixel data from a picture with the intention of using pictures to generate tilemap worlds. But I’ve stumbled at the second hurdle…

Testing the idea I did this:

var image = load("res://assets/HeightMap1024x1024.png")
var map = get_node("/root/home/MapPreview")
map.set_texture(image)
print(map.get_texture().get_data().get_pixel(100,100).to_html(false))

“MapPreview” is a texture frame control. The above code is triggered by pressing a button.

The picture appears in the frame, no problem. But the print() output, no matter where I target in the picture is always “000000” (The pixel at 100,100 is dark blue.)

Same happens in stable and a nightly build.

In 3.0 the debugger gives the following “Condition ’ !ptr ’ is true. returned: Color()” which I don’t understand.

What am I missing?

**UPDATE: I installed 2.1.4 and it works there. Still not working in 3.0 (a 2 day old nightly build from Service End for Bintray, JCenter, GoCenter, and ChartCenter | JFrog) . **
Also note 3.0 has changed TextureFrame to TextureRect
Promoted to a bug report: get.pixel always returns zero (black) · Issue #12245 · godotengine/godot · GitHub

The below code appears to work in 2.1.4, but gives the same error as you received in 3.0. I’m going to venture a guess get_data or get_pixel is broken in 3.0 for the time being; you may have to temporarily work around it or wait it out.

extends TextureFrame

func _ready():
	set_texture(load("res://icon.png"))

	var tex = get_texture()
	var size = tex.get_size()

	randomize()
	var x = randi() % int(size.x)
	var y = randi() % int(size.y)

	print(Vector2(x,y), ": ", tex.get_data().get_pixel(x,y).to_html(false))

Unless someone more knowledgeable than me has an answer…? (Edit: See Tapio Pyrhönen’s answer.)

aozasori | 2017-10-19 07:36

You seem to be correct that it’s a bug in 3.0. I just reinstalled 2.1.4 and it does work there. But does not work in 3.0.

I think I’m going to turn this into a bug report on GitHub, using your example code.

Thanks for your help.

monotonehell | 2017-10-20 00:05

:bust_in_silhouette: Reply From: Tapio Pyrhönen

You need to lock the data before messing with the pixels:

image.lock()
print(image.get_pixel(100,100).to_html(false))
image.unlock()

Thanks for the suggestion. I did try that, even though it seemed more to do with set_pixel. But it didn’t make any difference.

I just tried it again and same result.

monotonehell | 2017-10-20 00:02

That’s strange. It seemed to work when i tried it.

aozasori | 2017-10-20 01:06

Are you trying in 3.0? Because lock/unlock doesn’t exist on an image stream in that version.

monotonehell | 2017-10-20 01:49

Yes, in 3.0. I used lock on the ImageTexture obtained via get_texture, not on the ImageStream.

aozasori | 2017-10-20 01:54

Cool :slight_smile: but same response. :frowning:
I just tried this:

var myimage = load("res://icon.png")
var myicon = get_node("/root/home/TextureRect")
myicon.set_texture(myimage)
myicon.get_texture().lock()
print("Should be #ffffff: ",myicon.get_texture().get_data().get_pixel(32,32).to_html(false))
print("Should be #478cbf: ",myicon.get_texture().get_data().get_pixel(32,18).to_html(false))
myicon.get_texture().unlock()

And it still gives me the error “Invalid call: Nonexistent function ‘lock’ in base ‘StreamTexture’.”

I also tried to put the texture into an object and lock that. Not that I’d expect that to behave differently.
var tex = myicon.get_texture()
tex.lock()

Are you attempting this in a different way?

What build of 3.0 are you on? I’m using the nightly builds that Calinou is providing.

monotonehell | 2017-10-20 02:06

The issue might be, you are using get_texture multiple distinct times? I stored the result in a variable that i used for lock/unlock and get_data.

I’m using a version i built from source about a week ago, so it’s not strictly up to date.

aozasori | 2017-10-20 02:14

Don’t mean to be a doubter… but are sure you’re using 3.0? It’s just that you seem to be having a different experience to me.

Upon investigating the underlying data types via print(); it seems that V2.14’s TextureFrame is using datatype [ImageTexture:555] while V3.0’s TextureRect is using datatype [StreamTexture:1040].

Someone replied on the github issue saying that the new datatype can’t as yet poll pixel data from the graphics card. That would explain things somewhat.

Confusing and fun :wink:

monotonehell | 2017-10-20 02:28

Sorry, i miss-remembered what i did, exactly.
This is my exact code. I locked the data (which is what was done in the answer), not the texture.

extends TextureRect

func _ready():
	set_texture(load("res://icon.png"))

	var tex = get_texture()
	var size = tex.get_size()

	randomize()
	var x = randi() % int(size.x)
	var y = randi() % int(size.y)

	var data = tex.get_data()

	data.lock()
	print(Vector2(x,y), ": ", data.get_pixel(x,y).to_html(false))
	data.unlock()

aozasori | 2017-10-20 02:35

Thank you, aozasori.

With your example I think I understand it now. As you said, the get_pixel has to be performed on the data, and the data has to be locked.

V2.4.1 doesn’t need to be locked and you can get away with this:
print(map.get_texture().get_data().get_pixel(100,100).to_html(false))
Which won’t work in V3.0 even if locked.

monotonehell | 2017-10-20 04:22