diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index c0eb3efd44..bd7dc78ad7 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3894,6 +3894,12 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case EDITOR_TAKE_SCREENSHOT: { screenshot_timer->start(); } break; + case EDITOR_HDR_SCREENSHOT: { + bool ischecked = settings_menu->is_item_checked(settings_menu->get_item_index(EDITOR_HDR_SCREENSHOT)); + + settings_menu->set_item_checked(settings_menu->get_item_index(EDITOR_HDR_SCREENSHOT), !ischecked); + EditorSettings::get_singleton()->set_project_metadata("screenshot", "hdr_screenshot", !ischecked); + } break; case SETTINGS_PICK_MAIN_SCENE: { file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); List extensions; @@ -3993,15 +3999,20 @@ void EditorNode::_request_screenshot() { } void EditorNode::_screenshot(bool p_use_utc) { - String name = "editor_screenshot_" + Time::get_singleton()->get_datetime_string_from_system(p_use_utc).remove_char(':') + ".png"; + bool hdr_screenshot = EditorSettings::get_singleton()->get_project_metadata("screenshot", "hdr_screenshot", true); + bool hdr = DisplayServer::get_singleton()->window_is_hdr_output_enabled(DisplayServerEnums::MAIN_WINDOW_ID); + String name = "editor_screenshot_" + Time::get_singleton()->get_datetime_string_from_system(p_use_utc).remove_char(':') + (hdr && hdr_screenshot ? ".exr" : ".png"); String path = String("user://") + name; - if (!EditorRun::request_screenshot(callable_mp(this, &EditorNode::_save_screenshot_with_embedded_process).bind(path))) { + if (!EditorRun::request_screenshot(callable_mp(this, &EditorNode::_save_screenshot_with_embedded_process).bind(path), hdr_screenshot)) { _save_screenshot(path); } } void EditorNode::_save_screenshot_with_embedded_process(int64_t p_w, int64_t p_h, const String &p_emb_path, const Rect2i &p_rect, const String &p_path) { + bool hdr_screenshot = EditorSettings::get_singleton()->get_project_metadata("screenshot", "hdr_screenshot", true); + bool hdr = DisplayServer::get_singleton()->window_is_hdr_output_enabled(DisplayServerEnums::MAIN_WINDOW_ID); + Control *main_screen_control = editor_main_screen->get_control(); ERR_FAIL_NULL_MSG(main_screen_control, "Cannot get the editor main screen control."); Viewport *viewport = main_screen_control->get_viewport(); @@ -4010,15 +4021,36 @@ void EditorNode::_save_screenshot_with_embedded_process(int64_t p_w, int64_t p_h ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen."); Ref img = texture->get_image(); ERR_FAIL_COND_MSG(img.is_null(), "Cannot get an image from a viewport texture of the editor main screen."); - img->convert(Image::FORMAT_RGBA8); + if (hdr && hdr_screenshot) { + img->convert(Image::FORMAT_RGBAH); + } else { + Image::Format fmt = img->get_format(); + img->convert(Image::FORMAT_RGBA8); + if (fmt == Image::FORMAT_RGBAH) { + img->linear_to_srgb(); + } + } ERR_FAIL_COND(p_emb_path.is_empty()); Ref overlay = Image::load_from_file(p_emb_path); DirAccess::remove_absolute(p_emb_path); ERR_FAIL_COND_MSG(overlay.is_null(), "Cannot get an image from a embedded process."); - overlay->convert(Image::FORMAT_RGBA8); + if (hdr && hdr_screenshot) { + overlay->convert(Image::FORMAT_RGBAH); + } else { + Image::Format fmt = overlay->get_format(); + overlay->convert(Image::FORMAT_RGBA8); + if (fmt == Image::FORMAT_RGBAH) { + overlay->linear_to_srgb(); + } + } overlay->resize(p_rect.size.x, p_rect.size.y); img->blend_rect(overlay, Rect2i(0, 0, p_w, p_h), p_rect.position); - Error error = img->save_png(p_path); + Error error = OK; + if (hdr) { + error = img->save_exr(p_path, false, true, DisplayServer::get_singleton()->window_get_output_max_linear_value(DisplayServerEnums::MAIN_WINDOW_ID)); + } else { + error = img->save_png(p_path); + } ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'."); if (EDITOR_GET("interface/editor/behavior/automatically_open_screenshots")) { @@ -4034,8 +4066,24 @@ void EditorNode::_save_screenshot(const String &p_path) { Ref texture = viewport->get_texture(); ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen."); Ref img = texture->get_image(); + bool hdr = DisplayServer::get_singleton()->window_is_hdr_output_enabled(DisplayServerEnums::MAIN_WINDOW_ID); + bool hdr_screenshot = EditorSettings::get_singleton()->get_project_metadata("screenshot", "hdr_screenshot", true); + if (hdr && hdr_screenshot) { + img->convert(Image::FORMAT_RGBAH); + } else { + Image::Format fmt = img->get_format(); + img->convert(Image::FORMAT_RGBA8); + if (fmt == Image::FORMAT_RGBAH) { + img->linear_to_srgb(); + } + } ERR_FAIL_COND_MSG(img.is_null(), "Cannot get an image from a viewport texture of the editor main screen."); - Error error = img->save_png(p_path); + Error error = OK; + if (hdr) { + error = img->save_exr(p_path, false, true, DisplayServer::get_singleton()->window_get_output_max_linear_value(DisplayServerEnums::MAIN_WINDOW_ID)); + } else { + error = img->save_png(p_path); + } ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'."); if (EDITOR_GET("interface/editor/behavior/automatically_open_screenshots")) { @@ -8115,6 +8163,11 @@ void EditorNode::_build_settings_menu() { settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/take_screenshot"), EDITOR_TAKE_SCREENSHOT); settings_menu->set_item_tooltip(-1, TTRC("Screenshots are stored in the user data folder (\"user://\").")); + settings_menu->add_check_shortcut(ED_SHORTCUT("editor/hdr_screenshot", TTRC("Save HDR screenshots")), EDITOR_HDR_SCREENSHOT); + settings_menu->set_item_tooltip(-1, TTRC("If selected, screenshots are saved in EXR format, otherwise screenshots are converted to SDR and saved in PNG format.")); + settings_menu->set_item_checked(-1, EditorSettings::get_singleton()->get_project_metadata("screenshot", "hdr_screenshot", true)); + settings_menu->add_separator(); + settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/fullscreen_mode"), EDITOR_TOGGLE_FULLSCREEN); settings_menu->add_separator(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 337c7484a5..d6556da7e7 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -187,6 +187,7 @@ public: EDITOR_OPEN_SETTINGS, EDITOR_COMMAND_PALETTE, EDITOR_TAKE_SCREENSHOT, + EDITOR_HDR_SCREENSHOT, EDITOR_TOGGLE_FULLSCREEN, EDITOR_OPEN_DATA_FOLDER, EDITOR_OPEN_CONFIG_FOLDER, diff --git a/editor/run/editor_run.cpp b/editor/run/editor_run.cpp index 83998765b4..d7dbb3a328 100644 --- a/editor/run/editor_run.cpp +++ b/editor/run/editor_run.cpp @@ -195,9 +195,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie, const V return OK; } -bool EditorRun::request_screenshot(const Callable &p_callback) { +bool EditorRun::request_screenshot(const Callable &p_callback, bool p_hdr) { if (instance_rq_screenshot_callback) { - return instance_rq_screenshot_callback(p_callback); + return instance_rq_screenshot_callback(p_callback, p_hdr); } else { return false; } diff --git a/editor/run/editor_run.h b/editor/run/editor_run.h index 7c6b7909f9..f501062742 100644 --- a/editor/run/editor_run.h +++ b/editor/run/editor_run.h @@ -39,7 +39,7 @@ #include // INT_MAX typedef void (*EditorRunInstanceStarting)(int p_index, List &r_arguments); -typedef bool (*EditorRunInstanceRequestScreenshot)(const Callable &p_callback); +typedef bool (*EditorRunInstanceRequestScreenshot)(const Callable &p_callback, bool p_hdr); class EditorRun { public: @@ -79,7 +79,7 @@ public: int get_child_process_count() const { return pids.size(); } ProcessID get_current_process() const; - static bool request_screenshot(const Callable &p_callback); + static bool request_screenshot(const Callable &p_callback, bool p_hdr); static WindowPlacement get_window_placement(); diff --git a/editor/run/game_view_plugin.cpp b/editor/run/game_view_plugin.cpp index 97cd210e83..f9eada9fd5 100644 --- a/editor/run/game_view_plugin.cpp +++ b/editor/run/game_view_plugin.cpp @@ -361,7 +361,7 @@ void GameViewDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("hdr_state_received", PropertyInfo(Variant::ARRAY, "hdr_state"))); } -bool GameViewDebugger::add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect) { +bool GameViewDebugger::add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect, bool p_hdr) { bool found = false; for (Ref &I : sessions) { if (I->is_active()) { @@ -372,6 +372,7 @@ bool GameViewDebugger::add_screenshot_callback(const Callable &p_callaback, cons Array arr; arr.append(scr_rq_id); + arr.append(p_hdr); I->send_message("scene:rq_screenshot", arr); scr_rq_id++; found = true; @@ -487,12 +488,12 @@ void GameView::_instance_starting(int p_idx, List &r_arguments) { _update_arguments_for_instance(p_idx, r_arguments); } -bool GameView::_instance_rq_screenshot_static(const Callable &p_callback) { +bool GameView::_instance_rq_screenshot_static(const Callable &p_callback, bool p_hdr) { ERR_FAIL_NULL_V(singleton, false); - return singleton->_instance_rq_screenshot(p_callback); + return singleton->_instance_rq_screenshot(p_callback, p_hdr); } -bool GameView::_instance_rq_screenshot(const Callable &p_callback) { +bool GameView::_instance_rq_screenshot(const Callable &p_callback, bool p_hdr) { if (debugger.is_null() || window_wrapper->get_window_enabled() || !embedded_process || !embedded_process->is_embedding_completed()) { return false; } @@ -501,7 +502,7 @@ bool GameView::_instance_rq_screenshot(const Callable &p_callback) { #ifndef MACOS_ENABLED r.position -= embedded_process->get_window()->get_position(); #endif - return debugger->add_screenshot_callback(p_callback, r); + return debugger->add_screenshot_callback(p_callback, r, p_hdr); } void GameView::_show_update_window_wrapper() { diff --git a/editor/run/game_view_plugin.h b/editor/run/game_view_plugin.h index ddcc21dd9d..83b4deebc6 100644 --- a/editor/run/game_view_plugin.h +++ b/editor/run/game_view_plugin.h @@ -80,7 +80,7 @@ public: virtual bool capture(const String &p_message, const Array &p_data, int p_session) override; virtual bool has_capture(const String &p_capture) const override; - bool add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect); + bool add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect, bool p_hdr); void window_request_size(); void hdr_output_request_state(); @@ -237,8 +237,8 @@ class GameView : public VBoxContainer { void _play_pressed(); static void _instance_starting_static(int p_idx, List &r_arguments); void _instance_starting(int p_idx, List &r_arguments); - static bool _instance_rq_screenshot_static(const Callable &p_callback); - bool _instance_rq_screenshot(const Callable &p_callback); + static bool _instance_rq_screenshot_static(const Callable &p_callback, bool p_hdr); + bool _instance_rq_screenshot(const Callable &p_callback, bool p_hdr); void _stop_pressed(); void _embedding_completed(); void _embedding_failed(); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 9adf9b8943..cbbe16cb0d 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -527,7 +527,7 @@ Error SceneDebugger::_msg_transform_camera_3d(const Array &p_args) { // region Embedded process screenshot. Error SceneDebugger::_msg_rq_screenshot(const Array &p_args) { - ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA); Viewport *viewport = SceneTree::get_singleton()->get_root(); ERR_FAIL_NULL_V_MSG(viewport, ERR_UNCONFIGURED, "Cannot get a viewport from the main screen."); @@ -537,6 +537,8 @@ Error SceneDebugger::_msg_rq_screenshot(const Array &p_args) { ERR_FAIL_COND_V_MSG(img.is_null(), ERR_UNCONFIGURED, "Cannot get an image from a viewport texture of the main screen."); img->clear_mipmaps(); + bool hdr = DisplayServer::get_singleton()->window_is_hdr_output_enabled(DisplayServerEnums::MAIN_WINDOW_ID); + bool hdr_screenshot = p_args[1]; const String TEMP_DIR = OS::get_singleton()->get_temp_path(); uint32_t suffix_i = 0; String path; @@ -544,13 +546,17 @@ Error SceneDebugger::_msg_rq_screenshot(const Array &p_args) { String datetime = Time::get_singleton()->get_datetime_string_from_system().remove_chars("-T:"); datetime += itos(Time::get_singleton()->get_ticks_usec()); String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : ""); - path = TEMP_DIR.path_join("scr-" + suffix + ".png"); + path = TEMP_DIR.path_join("scr-" + suffix + (hdr && hdr_screenshot ? ".exr" : ".png")); if (!DirAccess::exists(path)) { break; } suffix_i += 1; } - img->save_png(path); + if (hdr && hdr_screenshot) { + img->save_exr(path, false, true, DisplayServer::get_singleton()->window_get_output_max_linear_value(DisplayServerEnums::MAIN_WINDOW_ID)); + } else { + img->save_png(path); + } Array arr; arr.append(p_args[0]);