Partial transparency blended with black in screenshot images

Godot Version

Godot v4.5.stable.official [876b29033]
Compatibility mode
MacOS 15.7.1

Issue

When using Godot’s get_viewport().get_texture().get_image().save_png() function to save screenshot images of a game with partially transparent visuals (such as gradients which fade to transparent) using a transparent background window, the partially transparent pixels are blended with black, resulting in a darker (and uglier) image:

fix_alpha_edges() or convert(Image.FORMAT_RGBA8) or linear_to_srgb() don’t work to fix this.

Hacky workaround

A hacky workaround discussed in this Reddit thread does work-- using a CanvasItemMaterial set to Premultiplied alpha blend mode allows the screenshot to be rendered correctly:

However, the in-game visuals then suffer greatly by having the partially transparent pixels show up as solid white instead.

Question

In my own game I implemented the hacky workaround so that the material is only applied for a split-second while taking the screenshot, but it’s still perceptible as a white flash, very unpleasant.
Any suggestions for implementing this workaround to make it less unpleasant for the player somehow?
The white flash is so disruptive. I wish I could somehow hide the game visuals from the player’s view while still taking the screenshot.

MRP

Unfortunately I don’t know the solution, but I’d like to add that something very similar and very annoying happens when using the engine’s “Movie Maker” mode, where the entire recording made using that mode is a LOT darker and looks really ugly unless manually modified to look super bright. Maybe it’s related, and perhaps fixing it would fix both the screenshots and the Movie Maker “bug”?

Can you post the first screenshot? The actual png image.

Sure, here is the default screenshot:

And here is the screenshot with the canvasitemmaterial premultiplied alpha blend mode workaround:
https://github.com/mellowminx/godot-4-5-transparent-bg-screenshot-test/blob/main/premultalpha%20screenshot.png

Ok I looked at your mrp. You’re plucking the image from the main screen which afaik means that you can’t get linear/hdr data so the image is RGBA8 with premultiplied alpha.

As a quick solution, you can simply un-premultiply the pixels by dividing RGB values by their alpha before saving. Skip the zero divisions.

1 Like

That is indeed annoying! :frowning:

Thank you for the pointer! It sounds too technical for me but it’s a start, I’ll try to research in this direction :slight_smile:

Not technical at all. Just iterate through image pixels and divide their value by their alpha.

# just before saving
for j in image_data.get_height():
		for i in image_data.get_width():
			var c = image_data.get_pixel(i, j)
			if c.a > 0:
				c.r /= c.a
				c.g /= c.a
				c.b /= c.a
			image_data.set_pixel(i, j, c)

It’s a bit strange that Godot’s Image class has premultiply_alpha() function but doesn’t have its reverse.

Omg! That worked perfectly. The screenshot came out even better than the premultiplied alpha version. Thank you so much!!!

I’ll update my github repository for this with a link here too :smiley: Thank you!

1 Like