Skip to content

Commit

Permalink
[3.x] Windows icon export improvements.
Browse files Browse the repository at this point in the history
Regenerate Windows icon on export to ensure correct icon size order.
Add support for using PNG/WebP/SVG files as an icon for Windows exports.
Allow using WebP/SVG files as icon for macOS exports.
Add option to select generated icons interpolation, and set default interpolation to Lanczos.
  • Loading branch information
bruvzg committed Nov 20, 2022
1 parent 0803b41 commit 44739a1
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 15 deletions.
19 changes: 11 additions & 8 deletions platform/iphone/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));

r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));

Vector<PluginConfigIOS> found_plugins = get_plugins();
for (int i = 0; i < found_plugins.size(); i++) {
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), found_plugins[i].name)), false));
Expand Down Expand Up @@ -872,7 +875,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key));
return ERR_UNCONFIGURED;
}
img->resize(side_size, side_size);
img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int()));
err = img->save_png(p_iconset_dir + info.export_name);
if (err) {
memdelete(da);
Expand All @@ -895,7 +898,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
}
if (img->get_width() != side_size || img->get_height() != side_size) {
add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2(side_size, side_size)));
img->resize(side_size, side_size);
img->resize(side_size, side_size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int()));
err = img->save_png(p_iconset_dir + info.export_name);
} else {
err = da->copy(icon_path, p_iconset_dir + info.export_name);
Expand Down Expand Up @@ -1029,9 +1032,9 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
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);
img->resize(info.height * aspect_ratio, info.height, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
} else {
img->resize(info.width, info.width / aspect_ratio);
img->resize(info.width, info.width / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
}
}
Ref<Image> new_img = memnew(Image);
Expand Down Expand Up @@ -1068,17 +1071,17 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
if (info.rotate) {
if (boot_logo_scale) {
if (info.width * aspect_ratio <= info.height) {
img_bs->resize(info.width * aspect_ratio, info.width);
img_bs->resize(info.width * aspect_ratio, info.width, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
} else {
img_bs->resize(info.height, info.height / aspect_ratio);
img_bs->resize(info.height, info.height / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
}
}
} else {
if (boot_logo_scale) {
if (info.height * aspect_ratio <= info.width) {
img_bs->resize(info.height * aspect_ratio, info.height);
img_bs->resize(info.height * aspect_ratio, info.height, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
} else {
img_bs->resize(info.width, info.width / aspect_ratio);
img_bs->resize(info.width, info.width / aspect_ratio, (Image::Interpolation)(p_preset->get("application/launch_screens_interpolation").operator int()));
}
}
}
Expand Down
11 changes: 6 additions & 5 deletions platform/osx/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
Ref<ImageTexture> logo;

void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);

Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path);
Expand Down Expand Up @@ -213,7 +213,8 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)

r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.icns,*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
Expand Down Expand Up @@ -352,7 +353,7 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, PoolVector<uint8_t> &p_sour
memcpy(&p_dest.write[ofs], result.ptr(), res_size);
}

void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
void EditorExportPlatformOSX::_make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
Ref<ImageTexture> it = memnew(ImageTexture);

Vector<uint8_t> data;
Expand Down Expand Up @@ -386,7 +387,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy?
copy->convert(Image::FORMAT_RGBA8);
copy->resize(icon_infos[i].size, icon_infos[i].size);
copy->resize(icon_infos[i].size, icon_infos[i].size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int()));

if (icon_infos[i].is_png) {
// Encode PNG icon.
Expand Down Expand Up @@ -1181,7 +1182,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
icon.instance();
icon->load(iconpath);
if (!icon->empty()) {
_make_icon(icon, data);
_make_icon(p_preset, icon, data);
}
}
}
Expand Down
145 changes: 143 additions & 2 deletions platform/windows/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "export.h"

#include "core/io/image_loader.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "editor/editor_export.h"
Expand All @@ -38,6 +39,7 @@
#include "platform/windows/logo.gen.h"

class EditorExportPlatformWindows : public EditorExportPlatformPC {
Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path);
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);

Expand All @@ -52,6 +54,131 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC {
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const;
};

Error EditorExportPlatformWindows::_process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path) {
static const uint8_t icon_size[] = { 16, 32, 48, 64, 128, 0 /*256*/ };

struct IconData {
PoolVector<uint8_t> data;
uint8_t pal_colors = 0;
uint16_t planes = 0;
uint16_t bpp = 32;
};

HashMap<uint8_t, IconData> images;
Error err;

if (p_src_path.get_extension() == "ico") {
FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err);
if (err != OK) {
return err;
}

// Read ICONDIR.
f->get_16(); // Reserved.
uint16_t icon_type = f->get_16(); // Image type: 1 - ICO.
uint16_t icon_count = f->get_16(); // Number of images.
if (icon_type != 1) {
f->close();
memdelete(f);

ERR_FAIL_V(ERR_CANT_OPEN);
}

for (uint16_t i = 0; i < icon_count; i++) {
// Read ICONDIRENTRY.
uint16_t w = f->get_8(); // Width in pixels.
uint16_t h = f->get_8(); // Height in pixels.
uint8_t pal_colors = f->get_8(); // Number of colors in the palette (0 - no palette).
f->get_8(); // Reserved.
uint16_t planes = f->get_16(); // Number of color planes.
uint16_t bpp = f->get_16(); // Bits per pixel.
uint32_t img_size = f->get_32(); // Image data size in bytes.
uint32_t img_offset = f->get_32(); // Image data offset.
if (w != h) {
continue;
}

// Read image data.
uint64_t prev_offset = f->get_position();
images[w].pal_colors = pal_colors;
images[w].planes = planes;
images[w].bpp = bpp;
images[w].data.resize(img_size);
PoolVector<uint8_t>::Write wr = images[w].data.write();
f->seek(img_offset);
f->get_buffer(wr.ptr(), img_size);
f->seek(prev_offset);
}
f->close();
memdelete(f);
} else {
Ref<Image> src_image;
src_image.instance();
err = ImageLoader::load_image(p_src_path, src_image.ptr());
ERR_FAIL_COND_V(err != OK || src_image->empty(), ERR_CANT_OPEN);
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
int size = (icon_size[i] == 0) ? 256 : icon_size[i];

Ref<Image> res_image = src_image->duplicate();
ERR_FAIL_COND_V(res_image.is_null() || res_image->empty(), ERR_CANT_OPEN);
res_image->resize(size, size, (Image::Interpolation)(p_preset->get("application/icon_interpolation").operator int()));
images[icon_size[i]].data = res_image->save_png_to_buffer();
}
}

uint16_t valid_icon_count = 0;
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
if (images.has(icon_size[i])) {
valid_icon_count++;
} else {
int size = (icon_size[i] == 0) ? 256 : icon_size[i];
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Icon size \"%d\" is missing."), size));
}
}
ERR_FAIL_COND_V(valid_icon_count == 0, ERR_CANT_OPEN);

FileAccess *fw = FileAccess::open(p_dst_path, FileAccess::WRITE, &err);
if (err != OK) {
return err;
}

// Write ICONDIR.
fw->store_16(0); // Reserved.
fw->store_16(1); // Image type: 1 - ICO.
fw->store_16(valid_icon_count); // Number of images.

// Write ICONDIRENTRY.
uint32_t img_offset = 6 + 16 * valid_icon_count;
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
if (images.has(icon_size[i])) {
const IconData &di = images[icon_size[i]];
fw->store_8(icon_size[i]); // Width in pixels.
fw->store_8(icon_size[i]); // Height in pixels.
fw->store_8(di.pal_colors); // Number of colors in the palette (0 - no palette).
fw->store_8(0); // Reserved.
fw->store_16(di.planes); // Number of color planes.
fw->store_16(di.bpp); // Bits per pixel.
fw->store_32(di.data.size()); // Image data size in bytes.
fw->store_32(img_offset); // Image data offset.

img_offset += di.data.size();
}
}

// Write image data.
for (size_t i = 0; i < sizeof(icon_size) / sizeof(icon_size[0]); ++i) {
if (images.has(icon_size[i])) {
const IconData &di = images[icon_size[i]];
PoolVector<uint8_t>::Read r = di.data.read();
fw->store_buffer(r.ptr(), di.data.size());
}
}
fw->close();
memdelete(fw);

return OK;
}

Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
if (p_preset->get("codesign/enable")) {
return _code_sign(p_preset, p_path);
Expand Down Expand Up @@ -111,7 +238,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));

r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/modify_resources"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
Expand Down Expand Up @@ -148,6 +276,14 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset
#endif

String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
String tmp_icon_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("_rcedit.ico");
if (!icon_path.empty()) {
if (_process_icon(p_preset, icon_path, tmp_icon_path) != OK) {
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), vformat(TTR("Invalid icon file \"%s\"."), icon_path));
icon_path = String();
}
}

String file_verion = p_preset->get("application/file_version");
String product_version = p_preset->get("application/product_version");
String company_name = p_preset->get("application/company_name");
Expand All @@ -161,7 +297,7 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset
args.push_back(p_path);
if (icon_path != String()) {
args.push_back("--set-icon");
args.push_back(icon_path);
args.push_back(tmp_icon_path);
}
if (file_verion != String()) {
args.push_back("--set-file-version");
Expand Down Expand Up @@ -205,6 +341,11 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset

String str;
Error err = OS::get_singleton()->execute(rcedit_path, args, true, nullptr, &str, nullptr, true);

if (FileAccess::exists(tmp_icon_path)) {
DirAccess::remove_file_or_error(tmp_icon_path);
}

if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) {
add_message(EXPORT_MESSAGE_WARNING, TTR("Resources Modification"), TTR("Could not start rcedit executable. Configure rcedit path in the Editor Settings (Export > Windows > rcedit), or disable \"Application > Modify Resources\" in the export preset."));
return err;
Expand Down

0 comments on commit 44739a1

Please sign in to comment.