Support compiling app icon sets. (#94)

* Pass through info and dependency info options to asset compile output.

Fixes writing out additional info plist and dependency info.

* Add initial implementation of compiling app icons.

Supports copying the images and writing the icon metadata to info
plist additions. Current implementation has two limitations:

 - All idioms are copied for all target platforms. For example, watch
   images are copied for asset compilation targeting a phone.
 - The size of images is not verified against the specified dimensions.
This commit is contained in:
Grant Paul 2016-07-21 15:17:16 -07:00 committed by Marc Salem
parent b9aae64b4e
commit f7dc2741e2
2 changed files with 131 additions and 8 deletions

View File

@ -10,6 +10,10 @@
#include <acdriver/Compile/AppIconSet.h>
#include <acdriver/CompileOutput.h>
#include <acdriver/Result.h>
#include <plist/Array.h>
#include <plist/Boolean.h>
#include <plist/Dictionary.h>
#include <plist/String.h>
#include <libutil/Filesystem.h>
using acdriver::Compile::AppIconSet;
@ -17,6 +21,50 @@ using acdriver::CompileOutput;
using acdriver::Result;
using libutil::Filesystem;
static plist::Dictionary *
IconsDictionary(plist::Dictionary *info, std::string const &key)
{
if (auto existing = info->value<plist::Dictionary>(key)) {
return existing;
} else {
auto icons = plist::Dictionary::New();
info->set(key, std::move(icons));
return info->value<plist::Dictionary>(key);
}
}
static std::string
IdiomSuffix(xcassets::Slot::Idiom idiom)
{
switch (idiom) {
case xcassets::Slot::Idiom::Universal:
return std::string();
case xcassets::Slot::Idiom::Phone:
return std::string();
case xcassets::Slot::Idiom::Pad:
return "~ipad";
case xcassets::Slot::Idiom::Desktop:
// TODO: no idiom suffix for desktop
return std::string();
case xcassets::Slot::Idiom::TV:
return "~tv";
case xcassets::Slot::Idiom::Watch:
return "~watch";
case xcassets::Slot::Idiom::Car:
return "~car";
}
abort();
}
struct IdiomHash
{
std::size_t operator()(xcassets::Slot::Idiom idiom) const
{
return static_cast<std::size_t>(idiom);
}
};
bool AppIconSet::
Compile(
std::shared_ptr<xcassets::Asset::AppIconSet> const &appIconSet,
@ -24,12 +72,85 @@ Compile(
CompileOutput *compileOutput,
Result *result)
{
result->document(
Result::Severity::Warning,
appIconSet->path(),
{ CompileOutput::AssetReference(appIconSet) },
"Not Implemented",
"app icon set not yet supported");
/*
* Copy the app icon images into the output.
*/
auto files = std::unordered_map<xcassets::Slot::Idiom, std::vector<std::string>, IdiomHash>();
if (appIconSet->images()) {
for (xcassets::Asset::AppIconSet::Image const &image : *appIconSet->images()) {
/*
* Verify the image has the required information.
*/
if (image.unassigned() || !image.fileName() || !image.imageSize() || !image.scale()) {
result->document(
Result::Severity::Warning,
appIconSet->path(),
{ CompileOutput::AssetReference(appIconSet) },
"Ambiguous Content",
"an icon in \"" + appIconSet->name().name() + "\" is unassigned");
continue;
}
return false;
/*
* Determine information about the icon image.
*/
xcassets::Slot::ImageSize const &size = *image.imageSize();
// TODO: verify the dimensions of the image are correct
std::string sizeSuffix = xcassets::Slot::ImageSize::String(size);
xcassets::Slot::Scale const &scale = *image.scale();
std::string scaleSuffix = std::string();
if (scale.value() != 1.0) {
scaleSuffix = "@" + xcassets::Slot::Scale::String(scale);
}
xcassets::Slot::Idiom idiom = image.idiom().value_or(xcassets::Slot::Idiom::Universal);
// TODO: skip images with idioms inappropriate for target platform
std::string idiomSuffix = IdiomSuffix(idiom);
/*
* Copy the icon image into the output.
*/
std::string source = appIconSet->path() + "/" + *image.fileName();
std::string destination = compileOutput->root() + "/" + appIconSet->name().name() + sizeSuffix + scaleSuffix + idiomSuffix + ".png";
compileOutput->copies().push_back({ source, destination });
/*
* Add the file to the output info.
*/
std::string name = appIconSet->name().name() + sizeSuffix;
files[idiom].push_back(name);
}
}
/*
* Build up the info about the icons.
*/
for (auto const &pair : files) {
/*
* Record details about the icon itself.
*/
auto primary = plist::Dictionary::New();
/* List the files for the icon. */
auto files = plist::Array::New();
for (std::string const &file : pair.second) {
files->append(plist::String::New(file));
}
primary->set("CFBundleIconFiles", std::move(files));
/* Record if the icon is pre-rendered. */
if (appIconSet->preRendered()) {
primary->set("UIPrerenderedIcon", plist::Boolean::New(true));
}
/*
* Store the icon in the additional info.
*/
std::string idiomSuffix = IdiomSuffix(pair.first);
plist::Dictionary *icons = IconsDictionary(compileOutput->additionalInfo(), "CFBundleIcons" + idiomSuffix);
icons->set("CFBundlePrimaryIcon", std::move(primary));
}
return true;
}

View File

@ -302,7 +302,9 @@ run(Filesystem *filesystem, Options const &options, Output *output, Result *resu
/*
* Write out the output.
*/
if (!compileOutput.write(filesystem, ext::nullopt, ext::nullopt, result)) {
ext::optional<std::string> partialInfoPlist = (!options.outputPartialInfoPlist().empty() ? ext::optional<std::string>(options.outputPartialInfoPlist()) : ext::nullopt);
ext::optional<std::string> dependencyInfo = (!options.exportDependencyInfo().empty() ? ext::optional<std::string>(options.exportDependencyInfo()) : ext::nullopt);
if (!compileOutput.write(filesystem, partialInfoPlist, dependencyInfo, result)) {
/* Error already reported. */
}
}