Dealing with textures is an entirely different matter, they live on your graphics card, not your RAM.
So here is what happens in your code:
get_data() downloads a copy of the texture from the graphics card and returns it as an
Image. Then you lock it, but the image is not stored anywhere after that, so it gets destroyed.
for row in texture_normal.get_height():
for column in texture_normal.get_width():
Then you iterate every pixel, and for each pixel, you are calling
get_data(). This once again downloads a copy of the entire texture as an
Image, which might be very slow, (unless Godot caches it?). Then you use
get_pixel() but because this is another instance of
Image, it will likely cause an error because that copy was never locked.
if texture_normal.get_data().get_pixel(column, row) == ColorN("white"):
texture_normal.get_data().set_pixel(column, row, ColorN("red"))
Here, there is more of the same. The texture is downloaded again and again, and more errors could occur.
And one last copy of it, which you try to unlock but it won't work because it was never locked in the first place.
Basically, you are working on downloaded copies of a texture, and that texture might not even be modifiable in the first place, if it's a
StreamTexture for example. Textures imported in your project are
StreamTexture by default.
If you want a texture to be modifiable, you need to use
ImageTexture. This type of texture allows you to create it from an
There doesn't seem to be an import option to make PNG files import as an
ImageTexture, so it seems like you will have to replace your
texture with a new instance of an
# Get the image ONCE
var image = texture_normal.get_data()
# Lock it
for row in image.get_height():
for column in image.get_width():
if image.get_pixel(column, row) == ColorN("white"):
image.set_pixel(column, row, ColorN("red"))
# At this point, you have an `Image` with replaced pixels.
# If you want to draw it on screen using a sprite or a mesh instance,
# you can create an ImageTexture from it.
texture_normal = ImageTexture.new()
# Then don't forget to update the `texture` property of whatever uses that texture
get_node("Sprite").texture = texture_normal