Files
agame/addons/godot_ai/utils/screenshot_encode.gd
2026-09-22 16:07:50 +08:00

46 lines
1.9 KiB
GDScript

@tool
class_name McpScreenshotEncode
extends RefCounted
## Shared downscale + PNG + base64 block for the screenshot paths (#716).
##
## Two call sites straddle the editor/game process boundary — the editor's
## take_screenshot (editor_handler) and the game-process autoload
## (runtime/game_helper) — and were maintained as manually synchronized
## copies. Pure static, no editor APIs, so it loads safely in the game
## process too.
## Downscale `image` in place so its longest edge is at most
## `max_resolution` (0 = no cap), then PNG-encode. Returns
## {base64, width, height, original_width, original_height}.
static func downscale_and_encode(image: Image, max_resolution: int, use_hdr_2d := false) -> Dictionary:
## Compatibility readback stays sRGB, including when HDR 2D is enabled.
## Image.linear_to_srgb requires RGB8/RGBA8: converting first quantizes
## linear darks and clamps values above 1 rather than tone-mapping them.
## Midgray therefore truncates approximately 54.6 -> 54 -> 126.
if use_hdr_2d and RenderingServer.get_current_rendering_method() != "gl_compatibility":
image.convert(Image.FORMAT_RGBA8)
image.linear_to_srgb()
var original_width := image.get_width()
var original_height := image.get_height()
if max_resolution > 0:
var longest := maxi(original_width, original_height)
if longest > max_resolution:
var scale := float(max_resolution) / float(longest)
## Clamp to 1px min: extreme aspect ratios at very small
## max_resolution could otherwise compute a zero dimension and
## crash image.resize().
var new_w := maxi(1, int(original_width * scale))
var new_h := maxi(1, int(original_height * scale))
image.resize(new_w, new_h, Image.INTERPOLATE_LANCZOS)
return {
"base64": Marshalls.raw_to_base64(image.save_png_to_buffer()),
"width": image.get_width(),
"height": image.get_height(),
"original_width": original_width,
"original_height": original_height,
}