+2 votes

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 https://bintray.com/calinou/godot/editor/2017-10-16.cc7f224#files) . **
**Also note 3.0 has changed TextureFrame to TextureRect

Promoted to a bug report: https://github.com/godotengine/godot/issues/12245

in Engine by (17 points)
edited by

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.)

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.

1 Answer

+8 votes
Best answer

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

image.lock()
print(image.get_pixel(100,100).to_html(false))
image.unlock()
by (152 points)
selected by

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.

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

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

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

Cool :) but same response. :(
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.

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.

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 ;)

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()

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.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.