Browse Source

[iOS] Sync the boot splash and the launch screen image scale modes

Jānis Kiršteins 5 months ago
parent
commit
0e266b0099

+ 17 - 0
core/os/os.cpp

@@ -222,6 +222,23 @@ uint64_t OS::get_embedded_pck_offset() const {
 	return 0;
 }
 
+// Default boot screen rect scale mode is "Keep Aspect Centered"
+Rect2 OS::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
+	Rect2 screenrect;
+	if (p_window_size.width > p_window_size.height) {
+		// Scale horizontally.
+		screenrect.size.y = p_window_size.height;
+		screenrect.size.x = p_imgrect_size.x * p_window_size.height / p_imgrect_size.y;
+		screenrect.position.x = (p_window_size.width - screenrect.size.x) / 2;
+	} else {
+		// Scale vertically.
+		screenrect.size.x = p_window_size.width;
+		screenrect.size.y = p_imgrect_size.y * p_window_size.width / p_imgrect_size.x;
+		screenrect.position.y = (p_window_size.height - screenrect.size.y) / 2;
+	}
+	return screenrect;
+}
+
 // Helper function to ensure that a dir name/path will be valid on the OS
 String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_paths) const {
 	String safe_dir_name = p_dir_name;

+ 2 - 0
core/os/os.h

@@ -161,6 +161,8 @@ public:
 	virtual void open_midi_inputs();
 	virtual void close_midi_inputs();
 
+	virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const;
+
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 
 	struct GDExtensionData {

+ 1 - 1
editor/export/editor_export_platform.cpp

@@ -1525,7 +1525,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
 		custom_list.append_array(export_plugins[i]->_get_export_features(Ref<EditorExportPlatform>(this), p_debug));
 	}
 
-	ProjectSettings::CustomMap custom_map;
+	ProjectSettings::CustomMap custom_map = get_custom_project_settings(p_preset);
 	if (path_remaps.size()) {
 		if (true) { //new remap mode, use always as it's friendlier with multiple .pck exports
 			for (int i = 0; i < path_remaps.size(); i += 2) {

+ 1 - 0
editor/export/editor_export_platform.h

@@ -336,6 +336,7 @@ public:
 	virtual void get_platform_features(List<String> *r_features) const = 0;
 	virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {}
 	virtual String get_debug_protocol() const { return "tcp://"; }
+	virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const { return HashMap<String, Variant>(); }
 
 	EditorExportPlatform();
 };

+ 34 - 12
platform/ios/export/export_plugin.cpp

@@ -198,6 +198,19 @@ static const DataCollectionInfo data_collect_purpose_info[] = {
 	{ "Other", "NSPrivacyCollectedDataTypePurposeOther" },
 };
 
+static const String export_method_string[] = {
+	"app-store",
+	"development",
+	"ad-hoc",
+	"enterprise"
+};
+static const String storyboard_image_scale_mode[] = {
+	"center",
+	"scaleAspectFit",
+	"scaleAspectFill",
+	"scaleToFill"
+};
+
 String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
 	if (p_preset) {
 		if (p_name == "application/app_store_team_id") {
@@ -402,19 +415,28 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
 }
 
+HashMap<String, Variant> EditorExportPlatformIOS::get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const {
+	HashMap<String, Variant> settings;
+
+	int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
+	String value;
+
+	switch (image_scale_mode) {
+		case 0: {
+			String logo_path = GLOBAL_GET("application/boot_splash/image");
+			bool is_on = GLOBAL_GET("application/boot_splash/fullsize");
+			// If custom logo is not specified, Godot does not scale default one, so we should do the same.
+			value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center";
+		} break;
+		default: {
+			value = storyboard_image_scale_mode[image_scale_mode - 1];
+		}
+	}
+	settings["ios/launch_screen_image_mode"] = value;
+	return settings;
+}
+
 void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
-	static const String export_method_string[] = {
-		"app-store",
-		"development",
-		"ad-hoc",
-		"enterprise"
-	};
-	static const String storyboard_image_scale_mode[] = {
-		"center",
-		"scaleAspectFit",
-		"scaleAspectFill",
-		"scaleToFill"
-	};
 	String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
 	String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
 	bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer" && dbg_sign_id != "iPhone Distribution");

+ 2 - 0
platform/ios/export/export_plugin.h

@@ -201,6 +201,8 @@ public:
 		return list;
 	}
 
+	virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const override;
+
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
 
 	virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;

+ 2 - 0
platform/ios/os_ios.h

@@ -132,6 +132,8 @@ public:
 
 	void on_enter_background();
 	void on_exit_background();
+
+	virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const override;
 };
 
 #endif // IOS_ENABLED

+ 57 - 0
platform/ios/os_ios.mm

@@ -86,6 +86,46 @@ void register_dynamic_symbol(char *name, void *address) {
 	OS_IOS::dynamic_symbol_lookup_table[String(name)] = address;
 }
 
+Rect2 fit_keep_aspect_centered(const Vector2 &p_container, const Vector2 &p_rect) {
+	real_t available_ratio = p_container.width / p_container.height;
+	real_t fit_ratio = p_rect.width / p_rect.height;
+	Rect2 result;
+	if (fit_ratio < available_ratio) {
+		// Fit height - we'll have horizontal gaps
+		result.size.height = p_container.height;
+		result.size.width = p_container.height * fit_ratio;
+		result.position.y = 0;
+		result.position.x = (p_container.width - result.size.width) * 0.5f;
+	} else {
+		// Fit width - we'll have vertical gaps
+		result.size.width = p_container.width;
+		result.size.height = p_container.width / fit_ratio;
+		result.position.x = 0;
+		result.position.y = (p_container.height - result.size.height) * 0.5f;
+	}
+	return result;
+}
+
+Rect2 fit_keep_aspect_covered(const Vector2 &p_container, const Vector2 &p_rect) {
+	real_t available_ratio = p_container.width / p_container.height;
+	real_t fit_ratio = p_rect.width / p_rect.height;
+	Rect2 result;
+	if (fit_ratio < available_ratio) {
+		// Need to scale up to fit width, and crop height
+		result.size.width = p_container.width;
+		result.size.height = p_container.width / fit_ratio;
+		result.position.x = 0;
+		result.position.y = (p_container.height - result.size.height) * 0.5f;
+	} else {
+		// Need to scale up to fit height, and crop width
+		result.size.width = p_container.height * fit_ratio;
+		result.size.height = p_container.height;
+		result.position.x = (p_container.width - result.size.width) * 0.5f;
+		result.position.y = 0;
+	}
+	return result;
+}
+
 OS_IOS *OS_IOS::get_singleton() {
 	return (OS_IOS *)OS::get_singleton();
 }
@@ -660,4 +700,21 @@ void OS_IOS::on_exit_background() {
 	}
 }
 
+Rect2 OS_IOS::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
+	String scalemodestr = GLOBAL_GET("ios/launch_screen_image_mode");
+
+	if (scalemodestr == "scaleAspectFit") {
+		return fit_keep_aspect_centered(p_window_size, p_imgrect_size);
+	} else if (scalemodestr == "scaleAspectFill") {
+		return fit_keep_aspect_covered(p_window_size, p_imgrect_size);
+	} else if (scalemodestr == "scaleToFill") {
+		return Rect2(Point2(), p_window_size);
+	} else if (scalemodestr == "center") {
+		return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
+	} else {
+		WARN_PRINT(vformat("Boot screen scale mode mismatch between iOS and Godot: %s not supported", scalemodestr));
+		return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
+	}
+}
+
 #endif // IOS_ENABLED

+ 1 - 12
servers/rendering/renderer_rd/renderer_compositor_rd.cpp

@@ -216,18 +216,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
 	Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height());
 	Rect2 screenrect;
 	if (p_scale) {
-		if (window_size.width > window_size.height) {
-			//scale horizontally
-			screenrect.size.y = window_size.height;
-			screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y;
-			screenrect.position.x = (window_size.width - screenrect.size.x) / 2;
-
-		} else {
-			//scale vertically
-			screenrect.size.x = window_size.width;
-			screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x;
-			screenrect.position.y = (window_size.height - screenrect.size.y) / 2;
-		}
+		screenrect = OS::get_singleton()->calculate_boot_screen_rect(window_size, imgrect.size);
 	} else {
 		screenrect = imgrect;
 		screenrect.position += ((window_size - screenrect.size) / 2.0).floor();