Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Improve iOS icon / loading screen export. #56861

Merged
merged 1 commit into from
Jan 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 69 additions & 82 deletions platform/iphone/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,17 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));

r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with Retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with Retina HD display

r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with Retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro

r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store

r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with Retina display

r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0));
Expand All @@ -160,8 +160,6 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));

r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false));

for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
}
Expand Down Expand Up @@ -463,26 +461,26 @@ struct IconInfo {
const char *actual_size_side;
const char *scale;
const char *unscaled_size;
bool is_required = false;
};

static const IconInfo icon_infos[] = {
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true },
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },

{ "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true },
{ "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true },

{ "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },

{ "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false },

{ "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false },

{ "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false },

{ "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false },
{ "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false }
// Home screen on iPhone
{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60" },
{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40" },
{ "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60" },

// Home screen on iPad
{ "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76" },
{ "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76" },
{ "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5" },

// App Store
{ "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024" },

// Spotlight
{ "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40" },
{ "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40" },
{ "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40" }
};

Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
Expand All @@ -497,47 +495,37 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
int side_size = String(info.actual_size_side).to_int();
String icon_path = p_preset->get(info.preset_key);
if (icon_path.length() == 0) {
if ((bool)p_preset->get("icons/generate_missing")) {
// Resize main app icon
icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
Ref<Image> img = memnew(Image);
Error err = ImageLoader::load_image(icon_path, img);
if (err != OK) {
ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
return ERR_UNCONFIGURED;
}
img->resize(side_size, side_size);
err = img->save_png(p_iconset_dir + info.export_name);
if (err) {
String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
ERR_PRINT(err_str.utf8().get_data());
return err;
}
} else {
if (info.is_required) {
String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset.";
ERR_PRINT(err_str);
return ERR_UNCONFIGURED;
} else {
String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset.";
WARN_PRINT(err_str);
}
continue;
// Resize main app icon
icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
Ref<Image> img = memnew(Image);
Error err = ImageLoader::load_image(icon_path, img);
if (err != OK) {
ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
return ERR_UNCONFIGURED;
}
img->resize(side_size, side_size);
err = img->save_png(p_iconset_dir + info.export_name);
if (err) {
String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
ERR_PRINT(err_str.utf8().get_data());
return err;
}
} else {
// Load custom icon
// Load custom icon and resize if required
Ref<Image> img = memnew(Image);
Error err = ImageLoader::load_image(icon_path, img);
if (err != OK) {
ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
return ERR_UNCONFIGURED;
}
if (img->get_width() != side_size || img->get_height() != side_size) {
ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'.");
return ERR_UNCONFIGURED;
WARN_PRINT("Icon (" + String(info.preset_key) + "): '" + icon_path + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(side_size) + "x" + String::num_int64(side_size) + ".");
img->resize(side_size, side_size);
err = img->save_png(p_iconset_dir + info.export_name);
} else {
err = da->copy(icon_path, p_iconset_dir + info.export_name);
}

err = da->copy(icon_path, p_iconset_dir + info.export_name);
if (err) {
memdelete(da);
String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
Expand Down Expand Up @@ -649,31 +637,45 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
LoadingScreenInfo info = loading_screen_infos[i];
String loading_screen_file = p_preset->get(info.preset_key);

Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");

if (loading_screen_file.size() > 0) {
// Load custom loading screens
// Load custom loading screens, and resize if required.
Ref<Image> img = memnew(Image);
Error err = ImageLoader::load_image(loading_screen_file, img);
if (err != OK) {
ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
return ERR_UNCONFIGURED;
}
if (img->get_width() != info.width || img->get_height() != info.height) {
ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
return ERR_UNCONFIGURED;
WARN_PRINT("Loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(info.width) + "x" + String::num_int64(info.height) + ".");
float aspect_ratio = (float)img->get_width() / (float)img->get_height();
if (boot_logo_scale) {
if (info.height * aspect_ratio <= info.width) {
img->resize(info.height * aspect_ratio, info.height);
} else {
img->resize(info.width, info.width / aspect_ratio);
}
}
Ref<Image> new_img = memnew(Image);
new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8);
new_img->fill(boot_bg_color);
_blend_and_rotate(new_img, img, false);
err = new_img->save_png(p_dest_dir + info.export_name);
} else {
err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
}
err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
if (err) {
memdelete(da);
String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'.";
ERR_PRINT(err_str.utf8().get_data());
return err;
}
} else if ((bool)p_preset->get("launch_screens/generate_missing")) {
} else {
// Generate loading screen from the splash screen
Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");

Ref<Image> img = memnew(Image);
img->create(info.width, info.height, false, Image::FORMAT_RGBA8);
img->fill(boot_bg_color);
Expand Down Expand Up @@ -713,9 +715,6 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen.";
WARN_PRINT(err_str.utf8().get_data());
}
} else {
String err_str = String("No loading screen (") + info.preset_key + ") specified.";
WARN_PRINT(err_str.utf8().get_data());
}
}
memdelete(da);
Expand Down Expand Up @@ -1751,18 +1750,6 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
valid = false;
}

for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
IconInfo info = icon_infos[i];
String icon_path = p_preset->get(info.preset_key);
if (icon_path.length() == 0) {
if (info.is_required) {
err += TTR("Required icon is not specified in the preset.") + "\n";
valid = false;
}
break;
}
}

const String etc_error = test_etc2();
if (!etc_error.is_empty()) {
valid = false;
Expand Down