diff --git a/include/compression_parser.h b/include/compression_parser.h index 408d5e5..75c0c41 100644 --- a/include/compression_parser.h +++ b/include/compression_parser.h @@ -52,9 +52,15 @@ struct TranscodeResult { int32_t height; }; +struct ImageSize { + size_t width; + size_t height; +}; + typedef TranscodeError (*ITranscodeImages) (const std::string &imagePath, const bool extAppend, std::string &outputPath, TranscodeResult &result); typedef bool (*ISetTranscodeOptions) (const std::string &optionJson, const std::string &optionJsonExclude); +typedef TranscodeError (*IScaleImage) (const std::string &imagePath, std::string &outputPath, ImageSize size); class CompressionParser { public: @@ -69,6 +75,8 @@ public: std::string PrintTransMessage(); bool GetDefaultCompress(); void SetOutPath(const std::string &path); + bool ScaleIconEnable(); + bool CheckAndScaleIcon(const std::string &src, const std::string &originDst, std::string &scaleDst); private: bool ParseContext(const cJSON *contextNode); bool ParseCompression(const cJSON *compressionNode); @@ -77,6 +85,7 @@ private: bool SetTranscodeOptions(const std::string &optionJson, const std::string &optionJsonExclude); TranscodeError TranscodeImages(const std::string &imagePath, const bool extAppend, std::string &outputPath, TranscodeResult &result); + TranscodeError ScaleImage(const std::string &imagePath, std::string &outputPath); std::vector ParsePath(const cJSON *pathNode); std::string ParseRules(const cJSON *rulesNode); std::string ParseJsonStr(const cJSON *node); diff --git a/include/file_entry.h b/include/file_entry.h index 3930ef6..0121f05 100644 --- a/include/file_entry.h +++ b/include/file_entry.h @@ -53,6 +53,7 @@ public: const FilePath &GetFilePath() const; static bool Exist(const std::string &path); static bool RemoveAllDir(const std::string &path); + static bool RemoveFile(const std::string &path); static bool CreateDirs(const std::string &path); static bool CopyFileInner(const std::string &src, const std::string &dst); static bool IsDirectory(const std::string &path); diff --git a/include/file_manager.h b/include/file_manager.h index 46c3151..5d19a4a 100644 --- a/include/file_manager.h +++ b/include/file_manager.h @@ -36,11 +36,13 @@ public: moduleName_ = moduleName; }; uint32_t MergeResourceItem(const std::map> &resourceInfos); + bool ScaleIcons(const std::string &output, const std::map> &iconMap); private: uint32_t ScanModule(const std::string &input, const std::string &output); uint32_t ParseReference(const std::string &output); void CheckAllItems(std::vector> &noBaseResource); + bool ScaleIcon(const std::string &output, ResourceItem &item); // id, resource items std::map> items_; diff --git a/include/reference_parser.h b/include/reference_parser.h index 322a03e..c6b1582 100644 --- a/include/reference_parser.h +++ b/include/reference_parser.h @@ -18,6 +18,7 @@ #include #include "id_worker.h" +#include "resource_data.h" #include "resource_item.h" namespace OHOS { @@ -31,6 +32,7 @@ public: uint32_t ParseRefInResourceItem(ResourceItem &resourceItem) const; uint32_t ParseRefInJsonFile(ResourceItem &resourceItem, const std::string &output, const bool isIncrement = false); uint32_t ParseRefInString(std::string &value, bool &update) const; + static std::map> &GetLayerIconIds(); private: bool ParseRefJson(const std::string &from, const std::string &to); bool ParseRefResourceItemData(const ResourceItem &resourceItem, std::string &data, bool &update) const; @@ -46,7 +48,10 @@ private: const IdWorker &idWorker_; static const std::map ID_REFS; static const std::map ID_OHOS_REFS; + static std::map> layerIconIds_; cJSON *root_; + bool isParsingMediaJson_; + int64_t mediaJsonId_{ INVALID_ID }; }; } } diff --git a/include/resource_data.h b/include/resource_data.h index 6347e6a..231a01c 100644 --- a/include/resource_data.h +++ b/include/resource_data.h @@ -49,10 +49,11 @@ const static std::string SOLUTIONS_ARROW = "> "; const static std::string LONG_PATH_HEAD = "\\\\?\\"; const static int32_t VERSION_MAX_LEN = 128; const static int32_t INT_TO_BYTES = sizeof(uint32_t); -static const int8_t RESTOOL_VERSION[VERSION_MAX_LEN] = { "Restool 5.011" }; +static const int8_t RESTOOL_VERSION[VERSION_MAX_LEN] = { "Restool 5.012" }; const static int32_t TAG_LEN = 4; static std::set g_resourceSet; const static int8_t THREAD_POOL_SIZE = 2; +const static int8_t INVALID_ID = -1; enum class KeyType { LANGUAGE = 0, diff --git a/include/resource_util.h b/include/resource_util.h index 9e2d180..245abd2 100644 --- a/include/resource_util.h +++ b/include/resource_util.h @@ -56,6 +56,13 @@ public: */ static bool RmoveAllDir(const std::string &path); + /** + * @brief remove file. + * @param path: input file. + * @return true if remove success, other false. + */ + static bool RmoveFile(const std::string &path); + /** * @brief open json file. * @param path: json file path. diff --git a/src/compression_parser.cpp b/src/compression_parser.cpp index 84d3d78..9852db4 100644 --- a/src/compression_parser.cpp +++ b/src/compression_parser.cpp @@ -95,6 +95,14 @@ uint32_t CompressionParser::Init() cerr << "Error: JSON file parsing failed, please check the JSON file." << NEW_LINE_PATH << filePath_ << endl; return RESTOOL_ERROR; } + cJSON *contextNode = cJSON_GetObjectItem(root_, "context"); + if (!ParseContext(contextNode)) { + cerr << NEW_LINE_PATH << filePath_ << endl; + return RESTOOL_ERROR; + } + if (!LoadImageTranscoder()) { + return RESTOOL_ERROR; + } cJSON *compressionNode = cJSON_GetObjectItem(root_, "compression"); if (!ParseCompression(compressionNode)) { return RESTOOL_ERROR; @@ -102,15 +110,11 @@ uint32_t CompressionParser::Init() if (!mediaSwitch_) { return RESTOOL_SUCCESS; } - cJSON *contextNode = cJSON_GetObjectItem(root_, "context"); cJSON *filtersNode = cJSON_GetObjectItem(compressionNode, "filters"); - if (!ParseContext(contextNode) || !ParseFilters(filtersNode)) { + if (!ParseFilters(filtersNode)) { cerr << NEW_LINE_PATH << filePath_ << endl; return RESTOOL_ERROR; } - if (!LoadImageTranscoder()) { - return RESTOOL_ERROR; - } string caches = outPath_; caches.append(SEPARATOR_FILE).append(CACHES_DIR); if (!ResourceUtil::CreateDirs(caches)) { @@ -375,6 +379,35 @@ TranscodeError CompressionParser::TranscodeImages(const string &imagePath, const return TranscodeError::SUCCESS; } +TranscodeError CompressionParser::ScaleImage(const std::string &imagePath, std::string &outputPath) +{ + if (!handle_) { + cout << "Warning: ScaleImage handle_ is nullptr." << endl; + return TranscodeError::LOAD_COMPRESS_FAILED; + } +#ifdef __WIN32 + IScaleImage iScaleImage = (IScaleImage)GetProcAddress(handle_, "TranscodeSLR"); +#else + IScaleImage iScaleImage = (IScaleImage)dlsym(handle_, "TranscodeSLR"); +#endif + if (!iScaleImage) { + cout << "Warning: Failed to get the 'TranscodeSLR'." << endl; + return TranscodeError::LOAD_COMPRESS_FAILED; + } + TranscodeError ret = (*iScaleImage)(imagePath, outputPath, { 512, 512 }); + if (ret != TranscodeError::SUCCESS) { + auto iter = ERRORCODEMAP.find(ret); + if (iter != ERRORCODEMAP.end()) { + cout << "Warning: ScaleImage failed, error message: " << iter->second << ", file path = " << imagePath + << endl; + } else { + cout << "Warning: ScaleImage failed" << ", file path = " << imagePath << endl; + } + return ret; + } + return TranscodeError::SUCCESS; +} + bool CompressionParser::CheckPath(const string &src, const vector &paths) { if (paths.size() == 1 && paths[0] == "true") { @@ -519,14 +552,14 @@ bool CompressionParser::CheckAndTranscode(const string &src, string &dst, string bool CompressionParser::CopyForTrans(const string &src, const string &originDst, const string &dst) { + string srcSuffix; + string dstSuffix; auto srcIndex = src.find_last_of("."); auto dstIndex = dst.find_last_of("."); - if (srcIndex == string::npos || dstIndex == string::npos) { - cerr << "Error: invalid copy path." << endl; - return false; + if (srcIndex != string::npos && dstIndex != string::npos) { + srcSuffix = src.substr(srcIndex + 1); + dstSuffix = dst.substr(dstIndex + 1); } - string srcSuffix = src.substr(srcIndex + 1); - string dstSuffix = dst.substr(dstIndex + 1); auto ret = false; if (srcSuffix == dstSuffix) { ret = ResourceUtil::CopyFileInner(src, dst); @@ -571,6 +604,40 @@ bool CompressionParser::CopyAndTranscode(const string &src, string &dst, const b CollectTime(totalCounts_, totalTime_, t2); return ret; } + +bool CompressionParser::CheckAndScaleIcon(const std::string &src, const std::string &originDst, std::string &scaleDst) +{ + scaleDst = src; + if (filePath_.empty() || outPath_.empty()) { + cout << "Info: compression path or out path is empty, unable to scale icon." << endl; + return true; + } + auto index = originDst.find_last_of(SEPARATOR_FILE); + if (index == string::npos) { + cerr << "Error: invalid output path." << NEW_LINE_PATH << originDst << endl; + return false; + } + uint32_t startIndex = outPath_.size() + RESOURCES_DIR.size() + 1; + string qualifierDir = originDst.substr(startIndex, index - startIndex); + string outputCache = outPath_ + SEPARATOR_FILE + CACHES_DIR + qualifierDir; + if (!ResourceUtil::CreateDirs(outputCache)) { + cerr << "Error: scale icon create output dir failed. dir = " << outputCache << endl; + return false; + } + string fileName = originDst.substr(index + 1); + string outputFile = outputCache + SEPARATOR_FILE + fileName; + auto ret = ScaleImage(src, outputFile); + if (ret == TranscodeError::SUCCESS) { + // if scale success, change src file to scale image + scaleDst = outputFile; + } + return true; +} + +bool CompressionParser::ScaleIconEnable() +{ + return !filePath_.empty() && !outPath_.empty() && handle_ != nullptr; +} } } } diff --git a/src/config_parser.cpp b/src/config_parser.cpp index 15b3d27..1d2f8aa 100644 --- a/src/config_parser.cpp +++ b/src/config_parser.cpp @@ -424,6 +424,11 @@ void ConfigParser::AddCheckNode(const string &key, uint32_t id) } else { result->second.emplace(id); } + auto layerIconIds = ReferenceParser::GetLayerIconIds(); + if (layerIconIds.find(id) != layerIconIds.end()) { + auto ids = layerIconIds[id]; + jsonCheckIds_[key].insert(ids.begin(), ids.end()); + } } } diff --git a/src/file_entry.cpp b/src/file_entry.cpp index d116512..18befeb 100644 --- a/src/file_entry.cpp +++ b/src/file_entry.cpp @@ -137,6 +137,15 @@ bool FileEntry::RemoveAllDir(const string &path) return RemoveAllDirInner(f); } +bool FileEntry::RemoveFile(const string &path) +{ + FileEntry f(path); + if (!f.Init()) { + return false; + } + return RemoveAllDirInner(f); +} + bool FileEntry::CreateDirs(const string &path) { return CreateDirsInner(path, 0); diff --git a/src/file_manager.cpp b/src/file_manager.cpp index 697fa7f..125d01a 100644 --- a/src/file_manager.cpp +++ b/src/file_manager.cpp @@ -15,6 +15,7 @@ #include "file_manager.h" #include +#include "compression_parser.h" #include #include "factory_resource_compiler.h" #include "file_entry.h" @@ -87,6 +88,85 @@ void FileManager::CheckAllItems(vector> &noBaseResource) } } } + +bool FileManager::ScaleIcons(const string &output, const std::map> &iconMap) +{ + if (!CompressionParser::GetCompressionParser()->ScaleIconEnable()) { + cout << "Info: scale icon is not enable." << endl; + return true; + } + std::set allIconIds; + for (auto &it : iconMap) { + if (it.first != "icon") { + continue; + } + allIconIds.insert(it.second.begin(), it.second.end()); + } + if (allIconIds.size() == 0) { + cout << "Info: no icons need to scale, icon ids size is 0." << endl; + return true; + } + for (auto &id : allIconIds) { + std::map>::iterator iter = items_.find(id); + if (iter == items_.end()) { + continue; + } + for (auto &item : iter->second) { + if (!ScaleIcon(output, item)) { + return false; + } + } + } + return true; +} + +bool FileManager::ScaleIcon(const string &output, ResourceItem &item) +{ + std::string media = "media"; + // item's data is short path for icon file, such as "entry/resources/base/media/app_icon.png" + const string currentData(reinterpret_cast(item.GetData()), item.GetDataLength()); + auto outIndex = currentData.find_last_of(SEPARATOR); + if (outIndex == string::npos) { + cerr << "Error: ScaleIcon invalid output name: " << currentData << endl; + return false; + } + // get current output file name and full path + string fileName = currentData.substr(outIndex + 1); + FileEntry::FilePath fullFilePath = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append(item.GetLimitKey()) + .Append(media).Append(fileName); + if (fullFilePath.GetExtension() == JSON_EXTENSION) { + cout << "Info: can't scale media json file." << endl; + return true; + } + const string fullOutPath = fullFilePath.GetPath(); + // delete current output file + if (!ResourceUtil::RmoveFile(fullOutPath)) { + cout << "Error: ScaleIcon RmoveFile failed: " << fullOutPath << endl; + return false; + } + // get origin icon output full path with the origin icon file name in src + std::string dst = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append(item.GetLimitKey()).Append(media) + .Append(item.GetName()).GetPath(); + // the origin full file in src + std::string scaleDst = item.GetFilePath(); + // scale icon + if (!CompressionParser::GetCompressionParser()->CheckAndScaleIcon(item.GetFilePath(), dst, scaleDst)) { + return false; + } + // compress scaled icon + if (!CompressionParser::GetCompressionParser()->CopyAndTranscode(scaleDst, dst)) { + return false; + } + string newFileName = FileEntry::FilePath(dst).GetFilename(); + std::string newData = moduleName_ + SEPARATOR + RESOURCES_DIR + SEPARATOR + item.GetLimitKey() + SEPARATOR + media + + SEPARATOR + newFileName; + if (!item.SetData(reinterpret_cast(newData.c_str()), newData.length())) { + cerr << "Error: ScaleIcon resource item set data fail, data: " << newData << NEW_LINE_PATH + << item.GetFilePath() << endl; + return false; + } + return true; +} } } } diff --git a/src/reference_parser.cpp b/src/reference_parser.cpp index bb050bb..cea1d96 100644 --- a/src/reference_parser.cpp +++ b/src/reference_parser.cpp @@ -53,7 +53,9 @@ const map ReferenceParser::ID_OHOS_REFS = { { "^\\$ohos:symbol:", ResType::SYMBOL } }; -ReferenceParser::ReferenceParser() : idWorker_(IdWorker::GetInstance()), root_(nullptr) +std::map> ReferenceParser::layerIconIds_; + +ReferenceParser::ReferenceParser() : idWorker_(IdWorker::GetInstance()), root_(nullptr), isParsingMediaJson_(false) { } @@ -113,14 +115,25 @@ uint32_t ReferenceParser::ParseRefInResourceItem(ResourceItem &resourceItem) con uint32_t ReferenceParser::ParseRefInJsonFile(ResourceItem &resourceItem, const string &output, const bool isIncrement) { string jsonPath; - if (resourceItem.GetResType() == ResType::MEDIA) { - jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR) - .Append(resourceItem.GetLimitKey()).Append("media").Append(resourceItem.GetName()).GetPath(); + ResType resType = resourceItem.GetResType(); + string resName = resourceItem.GetName(); + if (resType == ResType::MEDIA) { + jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append(resourceItem.GetLimitKey()).Append("media") + .Append(resName).GetPath(); + isParsingMediaJson_ = true; + mediaJsonId_ = idWorker_.GetId(resType, ResourceUtil::GetIdName(resName, resType)); + if (mediaJsonId_ != INVALID_ID) { + set set; + layerIconIds_[mediaJsonId_] = set; + } } else { - jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR) - .Append("base").Append("profile").Append(resourceItem.GetName()).GetPath(); + jsonPath = FileEntry::FilePath(output).Append(RESOURCES_DIR).Append("base").Append("profile").Append(resName) + .GetPath(); } - if (!ParseRefJson(resourceItem.GetFilePath(), jsonPath)) { + bool parseJsonRet = ParseRefJson(resourceItem.GetFilePath(), jsonPath); + isParsingMediaJson_ = false; + mediaJsonId_ = INVALID_ID; + if (!parseJsonRet) { return RESTOOL_ERROR; } @@ -274,6 +287,10 @@ bool ReferenceParser::ParseRefImpl(string &key, const map &refs if (regex_search(key, result, regex(ref.first))) { string name = key.substr(result[0].str().length()); int64_t id = idWorker_.GetId(ref.second, name); + if (!isSystem && ref.second == ResType::MEDIA && mediaJsonId_ != 0 + && layerIconIds_.find(mediaJsonId_) != layerIconIds_.end()) { + layerIconIds_[mediaJsonId_].insert(id); + } if (isSystem) { id = idWorker_.GetSystemId(ref.second, name); } @@ -314,6 +331,11 @@ bool ReferenceParser::ParseRefJsonImpl(cJSON *node, bool &needSave) const } return true; } + +std::map> &ReferenceParser::GetLayerIconIds() +{ + return layerIconIds_; +} } } } diff --git a/src/resource_pack.cpp b/src/resource_pack.cpp index a1cd9f8..b95ce67 100644 --- a/src/resource_pack.cpp +++ b/src/resource_pack.cpp @@ -308,6 +308,10 @@ uint32_t ResourcePack::PackNormal() return RESTOOL_ERROR; } + if (!FileManager::GetInstance().ScaleIcons(packageParser_.GetOutput(), configJson_.GetCheckNode())) { + return RESTOOL_ERROR; + } + if (packageParser_.GetIconCheck()) { CheckConfigJson(); } diff --git a/src/resource_util.cpp b/src/resource_util.cpp index cc9aeca..1a59c9e 100644 --- a/src/resource_util.cpp +++ b/src/resource_util.cpp @@ -73,6 +73,11 @@ bool ResourceUtil::RmoveAllDir(const string &path) return FileEntry::RemoveAllDir(path); } +bool ResourceUtil::RmoveFile(const string &path) +{ + return FileEntry::RemoveFile(path); +} + bool ResourceUtil::OpenJsonFile(const string &path, cJSON **root) { ifstream ifs(FileEntry::AdaptLongPath(path), ios::binary);