Drawing and then saving a png file?

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

I am trying to open a .png file in Godot, then draw on it, then save the result as a new .png file.
When I run my code, I see the png file I’ve chosen, and I see the arc I’ve drawn over it, and it saves a png file to the location I picked, BUT the file saved doesn’t have the arc I’ve drawn on it. It’s just a copy of the original, unaltered png file.

Here is my code

extends TextureRect


var page_path = 'user://my_drawings/a_drawing.png'



func _ready():
	
	##################LOADING A PNG FILE TO A TEXTURE RECT
	#here is the path to the png image...
	print('the page path is ',page_path)
	#then create an empty Image object...
	var image = Image.new()
	#load the .png file to it...
	var err = image.load(page_path)
	if err != OK:
		print('page image load failed!')
	#then create a new ImageTexture object...
	var image_texture = ImageTexture.new()
	#create it from the Image you made earlier...
	image_texture.create_from_image(image, Image.FORMAT_RGBA8)
	#and assign it to the TextureRect Object...
	texture = image_texture
	
	
	
	##################DRAWING MORE STUFF ONTO THE TEXTURE RECT...
	#don't know what to do here...
	#the idea is just to test drawing something on it...to see the difference...
	
	
#here's an arc i'm drawing on top of it to test saving the result...
func draw_circle_arc(center, radius, angle_from, angle_to, color):
	var nb_points = 32
	var points_arc = PoolVector2Array()
	
	for i in range(nb_points + 1):
		var angle_point = deg2rad(angle_from + i * (angle_to-angle_from) / nb_points - 90)
		points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
		
	for index_point in range(nb_points):
		draw_line(points_arc[index_point], points_arc[index_point + 1], color)

#_draw should only happen once...so i'll test the save process after _draw is called...
func _draw():
	var center = Vector2(100, 100)
	var radius = 80
	var angle_from = 75
	var angle_to = 195
	var color = Color(1.0, 0.0, 0.0)
	draw_circle_arc(center, radius, angle_from, angle_to, color)
	
	###################SAVING THE TEXTURERECT TEXTURE AS A PNG FILE...
	#now, let's see if we can save the texture located in the drawpad to a new file...
	var export_img = get_texture().get_data()
	export_img.flip_y()
	var export_path = 'user://export_img.png'
	export_img.save_png(export_path)
	print('export img saved!')

TL:DR
What do I need to do in order to

  1. Load a .png file into my Godot game (it has an alpha channel)
  2. draw new stuff onto it
  3. save the result as a new png file (which also has an alpha channel).

I don’t want to save the entire screen (like a screenshot) as a png. I just want to save the image as it is displayed by the TextureRect (it’s in a particular region of my overall screen). Plus I want to preserve the alpha channel in the original image, and taking a screenshot of everything would get rid of that precious alpha channel.

It seems that you did only draw on the screen but not into the texture/image.

You can change the contents of an Image using set_pixel() but this is probably to tedious.

You could also render the texture and draw to a viewport and get & save the image data of that viewport. That view port can either be a custom one (i.e. in the resolution of the source image) or just the default (=screen).

wombatstampede | 2020-03-23 10:41

:bust_in_silhouette: Reply From: Black Monkey

Does anyone get this to work?
I’m trying to do the samething.
If I replace the viewport with this texture, I can draw to a texture and save it to disk,
but I loose transparency.
…a bit lost…

I really like this solution, going to give it a try and I’ll post an answer if I can figure it out!

chugwig | 2020-08-24 23:07

It’s been a while, but here is the solution

image.convert(Image.FORMAT_RGBA8)

GaidamakUA | 2021-05-08 11:30

:bust_in_silhouette: Reply From: chugwig

Here’s a way to record a viewport (the root viewport by default) to a PNG file. From there it’s just about putting everything in the scene however you want it.

extends Node


export(NodePath) var viewport_path = null


onready var target_viewport = get_node(viewport_path) if viewport_path else get_tree().root.get_viewport()


func save_to(path):
	var img = target_viewport.get_texture().get_data()
	img.flip_y()
	return img.save_png(path)
	
:bust_in_silhouette: Reply From: chugwig

Here’s a way to record a viewport (the root viewport by default) to a PNG file. From there it’s just about putting everything in the scene with the appropriate blending modes.

extends Node


export(NodePath) var viewport_path = null


onready var target_viewport = get_node(viewport_path) if viewport_path else get_tree().root.get_viewport()


func save_to(path):
	var img = target_viewport.get_texture().get_data()
	img.flip_y()
	return img.save_png(path)