I'm trying to make an image of the content of a viewport, but fail. It appears to be a bug with ViewportTexture.get_data(). Before I post it in the issue tracker, I'll just check if someone else has had the same problem.

extends Node2D
    
func _ready():
   	# Setting sprite texture directly to the viewport's texture works
   	var vp_texture = $Viewport.get_texture() 
   	$ViaTextureSprite.texture = vp_texture # works, the content from the viewport shows up in the sprite

   	# Getting the data doesn't work	
   	var image = vp_texture.get_data() # returns blank data
   	print(image.data["data"].hex_encode()) # prints all zeros

	# Creating an image texture in this way works (I have tested by creating an Image from scratch)
	# However, here it gets feed a blank image
   	var image_texture = ImageTexture.new()
   	image_texture.create_from_image(image)
   	$ViaGetDataSprite.texture = image_texture # blank image in sprite
   	pass 

I tried to put in lock and unlock, but it didn't help.

	var image = vp_texture.get_data()
	image.lock()
	print(image.data["data"].hex_encode()) 
	var image_texture = ImageTexture.new()
	image_texture.create_from_image(image)
	image.unlock()

To lock the image, it must first exist. And it get's created in the vp_texture.get_data() call. However, the error seems to be in that function, get_data(), since it returns all zeros. And get_data() is a function of the ViewportTexture class, to retrieve (supposedly) the bytes making up the texture drawn on the viewport canvas.

The Image class also has a get_data() but that's just a getter for it's data poperty.

I think lock (and unlock) is only used in conjunction with the Image functions set_pixel and get_pixel, in order to manually set the content of the image.

You may need to add:

$Viewport.set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME) yield(get_tree(), "idle_frame") yield(get_tree(), "idle_frame")

so the Viewport has a bit to take populate the viewport texture. The code above also sets the Viewport to update in the next frame, which may or may not be necessary. You will want to make sure the Viewport update mode is set to something where it will update prior to trying to get the data though, as otherwise there won't be a texture to grab.

Thanks, that didn't work at first but when I changed to Viewport.UPDATE_ALWAYS, it worked. So it was the update mode which needed to be changed ,and the two extra yields added. I tested to remove them after the addition of UPDATE_ALWAYS, but then it stopped working. And only removing one also made it fail, so both of them are needed. Strange, I would never had found that solution!

So this code works:

extends Node2D

func _ready():
	# Setting sprite texture directly to the viewport's texture works
	var vp_texture = $Viewport.get_texture() 
	$ViaTextureSprite.texture = vp_texture 
	
	$Viewport.set_update_mode(Viewport.UPDATE_ALWAYS)
	yield(get_tree(), "idle_frame")
	yield(get_tree(), "idle_frame")	

	# Getting the data and creating an image texture for the sprite	
	var image = vp_texture.get_data() 
	var image_texture = ImageTexture.new()
	image_texture.create_from_image(image)
	$ViaGetDataSprite.texture = image_texture        

I may add that the reason for using the second method via the image texture is that the first method, by setting the viewport's texture directly to the sprite's texture, seems to be dynamic in nature. If you change the content of the viewport and set another sprite to this, then the first sprite has changed as well which may not be what you want.