diff --git a/include/compression_parser.h b/include/compression_parser.h index bcc69ee..da422c1 100644 --- a/include/compression_parser.h +++ b/include/compression_parser.h @@ -17,11 +17,40 @@ #define OHOS_RESTOOL_COMPRESSION_PARSER_H #include +#ifdef __WIN32 +#include +#else +#include +#endif #include "resource_util.h" namespace OHOS { namespace Global { namespace Restool { + +enum class TranscodeError { + SUCCESS = 0, + IMAGE_ERROR, + IMAGE_RESOLUTION_NOT_MATCH, + ANIMATED_IMAGE_SKIP, + MALLOC_FAILED, + ENCODE_ASTC_FAILED, + SUPER_COMPRESS_FAILED, + LOAD_COMPRESS_FAILED +}; + +struct TranscodeResult { + int64_t timeCostMs; + size_t originSize; + int32_t size; + int32_t width; + int32_t height; +}; + +typedef TranscodeError (*ITranscodeImages) (const std::string &imagePath, + std::string &outputPath, TranscodeResult &result); +typedef bool (*ISetTranscodeOptions) (const std::string &optionJson); + class CompressionParser { public: static std::shared_ptr GetCompressionParser(const std::string &filePath); @@ -30,14 +59,31 @@ public: explicit CompressionParser(const std::string &filePath); virtual ~CompressionParser(); uint32_t Init(); + bool CopyAndTranscode(const std::string &src, std::string &dst); private: bool ParseContext(const cJSON *contextNode); bool ParseCompression(const cJSON *compressionNode); - bool ParseFilter(const cJSON *filterNode); + bool ParseFilters(const cJSON *filtersNode); + bool LoadImageTranscoder(); + bool SetTranscodeOptions(const std::string &optionJson); + TranscodeError TranscodeImages(const std::string &imagePath, std::string &outputPath, TranscodeResult &result); + std::vector ParsePath(const cJSON *pathNode); + std::vector ParseRules(const cJSON *rulesNode); + std::string ParseJsonStr(const cJSON *node); + bool CheckPath(const std::string &src, const std::vector &paths); + bool IsInPath(const std::string &src, const std::shared_ptr &compressFilter); + bool IsInExcludePath(const std::string &src, const std::shared_ptr &compressFilter); + std::string GetOptionsString(const std::shared_ptr &compressFilter, int type); std::string filePath_; std::string extensionPath_; + std::vector> compressFilters_; bool mediaSwitch_; cJSON *root_; +#ifdef __WIN32 + HMODULE handle_ = nullptr; +#else + void *handle_ = nullptr; +#endif }; } } diff --git a/include/generic_compiler.h b/include/generic_compiler.h index 669ac2b..0264384 100644 --- a/include/generic_compiler.h +++ b/include/generic_compiler.h @@ -27,11 +27,11 @@ public: virtual ~GenericCompiler(); protected: uint32_t CompileSingleFile(const FileInfo &fileInfo) override; - bool PostFile(const FileInfo &fileInfo); + bool PostMediaFile(const FileInfo &fileInfo, const std::string &output); private: std::string GetOutputFilePath(const FileInfo &fileInfo) const; bool IsIgnore(const FileInfo &fileInfo) const; - bool CopyFile(const FileInfo &fileInfo) const; + bool CopyMediaFile(const FileInfo &fileInfo, std::string &output); }; } } diff --git a/include/resource_data.h b/include/resource_data.h index 8b410b7..83a7780 100644 --- a/include/resource_data.h +++ b/include/resource_data.h @@ -33,6 +33,11 @@ const static std::string RES_FILE_DIR = "resfile"; const static std::string ID_DEFINED_FILE = "id_defined.json"; const static std::string RESOURCE_INDEX_FILE = "resources.index"; const static std::string JSON_EXTENSION = ".json"; +#ifdef __WIN32 +const static std::string SEPARATOR_FILE = "\\"; +#else +const static std::string SEPARATOR_FILE = "/"; +#endif const static std::string SEPARATOR = "/"; const static std::string WIN_SEPARATOR = "\\"; const static std::string NEW_LINE_PATH = "\nat "; @@ -200,6 +205,14 @@ struct ResourceId { std::string name; }; +struct CompressFilter { + std::vector path; + std::vector excludePath; + std::vector rules; + std::vector expandRules; + std::string method; +}; + const std::map g_copyFileMap = { { RAW_FILE_DIR, ResType::RAW }, { RES_FILE_DIR, ResType::RES }, diff --git a/include/resource_util.h b/include/resource_util.h index 0577a6c..d2893ba 100644 --- a/include/resource_util.h +++ b/include/resource_util.h @@ -122,7 +122,7 @@ public: * @param dst: destination file path * @return true if success, other false */ - static bool CopyFleInner(const std::string &src, const std::string &dst); + static bool CopyFileInner(const std::string &src, const std::string &dst); /** * @brief create directories diff --git a/src/compression_parser.cpp b/src/compression_parser.cpp index d89602b..045a6ac 100644 --- a/src/compression_parser.cpp +++ b/src/compression_parser.cpp @@ -14,6 +14,8 @@ */ #include "compression_parser.h" + +#include #include #include #include "restool_errors.h" @@ -24,7 +26,22 @@ namespace Restool { using namespace std; static shared_ptr compressionParseMgr = nullptr; static once_flag compressionParserMgrFlag; - + +constexpr int OPT_TYPE_ONE = 1; +constexpr int OPT_TYPE_TWO = 2; +constexpr int OPT_TYPE_THREE = 3; +constexpr int OPT_SIZE_ONE = 1; +constexpr int OPT_SIZE_TWO = 2; +const map ERRORCODEMAP = { + { TranscodeError::SUCCESS, "SUCCESS" }, + { TranscodeError::IMAGE_RESOLUTION_NOT_MATCH, "IMAGE_RESOLUTION_NOT_MATCH" }, + { TranscodeError::ANIMATED_IMAGE_SKIP, "ANIMATED_IMAGE_SKIP" }, + { TranscodeError::MALLOC_FAILED, "MALLOC_FAILED" }, + { TranscodeError::ENCODE_ASTC_FAILED, "ENCODE_ASTC_FAILED" }, + { TranscodeError::SUPER_COMPRESS_FAILED, "SUPER_COMPRESS_FAILED" }, + { TranscodeError::LOAD_COMPRESS_FAILED, "LOAD_COMPRESS_FAILED" }, +}; + CompressionParser::CompressionParser() : filePath_(""), extensionPath_(""), mediaSwitch_(false), root_(nullptr) { @@ -40,6 +57,17 @@ CompressionParser::~CompressionParser() if (root_) { cJSON_Delete(root_); } +#ifdef __WIN32 + if (handle_) { + FreeLibrary(handle_); + handle_ = nullptr; + } +#else + if (handle_) { + dlclose(handle_); + handle_ = nullptr; + } +#endif } shared_ptr CompressionParser::GetCompressionParser(const string &filePath) @@ -75,11 +103,14 @@ uint32_t CompressionParser::Init() return RESTOOL_SUCCESS; } cJSON *contextNode = cJSON_GetObjectItem(root_, "context"); - cJSON *filterNode = cJSON_GetObjectItem(compressionNode, "filter"); - if (!ParseContext(contextNode) || !ParseFilter(filterNode)) { + cJSON *filtersNode = cJSON_GetObjectItem(compressionNode, "filters"); + if (!ParseContext(contextNode) || !ParseFilters(filtersNode)) { cerr << NEW_LINE_PATH << filePath_ << endl; return RESTOOL_ERROR; } + if (!LoadImageTranscoder()) { + return RESTOOL_ERROR; + } return RESTOOL_SUCCESS; } @@ -145,23 +176,23 @@ bool CompressionParser::ParseContext(const cJSON *contextNode) return true; } -bool CompressionParser::ParseFilter(const cJSON *filterNode) +bool CompressionParser::ParseFilters(const cJSON *filtersNode) { - if (!filterNode) { - cerr << "Error: if image transcoding is supported, the 'filter' node cannot be empty."; + if (!filtersNode) { + cerr << "Error: if image transcoding is supported, the 'filters' node cannot be empty."; return false; } - if (!cJSON_IsArray(filterNode)) { - cerr << "Error: 'filter' must be array."; + if (!cJSON_IsArray(filtersNode)) { + cerr << "Error: 'filters' must be array."; return false; } - if (cJSON_GetArraySize(filterNode) == 0) { - cerr << "Error: 'filter' value cannot be empty."; + if (cJSON_GetArraySize(filtersNode) == 0) { + cerr << "Error: 'filters' value cannot be empty."; return false; } - for (cJSON *item = filterNode->child; item; item = item->next) { + for (cJSON *item = filtersNode->child; item; item = item->next) { if (!cJSON_IsObject(item)) { - cerr << "Error: 'filter' value type must be object."; + cerr << "Error: 'filters' value type must be object."; return false; } cJSON *methodNode = cJSON_GetObjectItem(item, "method"); @@ -173,9 +204,227 @@ bool CompressionParser::ParseFilter(const cJSON *filterNode) cerr << "Error: 'method' must be object."; return false; } + shared_ptr compressFilter = make_shared(); + compressFilter->method = "\"method\":" + ParseJsonStr(methodNode); + cJSON *pathNode = cJSON_GetObjectItem(item, "path"); + compressFilter->path = ParsePath(pathNode); + cJSON *excludePathNode = cJSON_GetObjectItem(item, "exclude_path"); + compressFilter->excludePath = ParsePath(excludePathNode); + cJSON *rulesNode = cJSON_GetObjectItem(item, "rules_orignal"); + compressFilter->rules = ParseRules(rulesNode); + cJSON *expandRulesNode = cJSON_GetObjectItem(item, "rules_union"); + compressFilter->expandRules = ParseRules(expandRulesNode); + compressFilters_.emplace_back(compressFilter); } return true; } + +vector CompressionParser::ParseRules(const cJSON *rulesNode) +{ + vector res; + if (!rulesNode || !cJSON_IsObject(rulesNode)) { + cerr << "Error: ParseRules fail."; + return res; + } + for (cJSON *item = rulesNode->child; item; item = item->next) { + if (!item || !cJSON_IsArray(item)) { + continue; + } + string name(item->string); + res.emplace_back("\"" + name + "\":" + ParseJsonStr(item)); + } + return res; +} + +vector CompressionParser::ParsePath(const cJSON *pathNode) +{ + vector res; + if (!pathNode || !cJSON_IsArray(pathNode)) { + cerr << "Error: ParsePath fail."; + return res; + } + for (cJSON *item = pathNode->child; item; item = item->next) { + if (!item || !cJSON_IsString(item)) { + continue; + } + res.emplace_back(item->valuestring); + } + return res; +} + +string CompressionParser::ParseJsonStr(const cJSON *node) +{ + if (!node) { + return ""; + } + char *jsonString = cJSON_Print(node); + string res(jsonString); + free(jsonString); + return res; +} + +bool CompressionParser::LoadImageTranscoder() +{ +#ifdef __WIN32 + if (!handle_) { + handle_ = LoadLibrary(TEXT(extensionPath_.c_str())); + if (!handle_) { + cerr << "Error: open '" << extensionPath_.c_str() << "' fail." << endl; + cerr << "Error: LoadLibrary failed with error: " << GetLastError() << endl; + return false; + } + } +#else + if (!handle_) { + handle_ = dlopen(extensionPath_.c_str(), RTLD_LAZY); + if (!handle_) { + cerr << "Error: open '" << extensionPath_.c_str() << "' fail." << endl; + cerr << "Error: dlopen failed with error: " << dlerror() << endl; + return false; + } + } +#endif + return true; +} + +bool CompressionParser::SetTranscodeOptions(const string &optionJson) +{ + if (!handle_) { + cerr << "Error: handle_ is nullptr."<< endl; + return false; + } +#ifdef __WIN32 + ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)GetProcAddress(handle_, "SetTranscodeOptions"); +#else + ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)dlsym(handle_, "SetTranscodeOptions"); +#endif + if (!iSetTranscodeOptions) { + cerr << "Error: Failed to get the 'SetTranscodeOptions'."<< endl; + return false; + } + bool ret = (*iSetTranscodeOptions)(optionJson); + if (!ret) { + cerr << "Error: SetTranscodeOptions failed."<< endl; + return false; + } + return true; +} + +TranscodeError CompressionParser::TranscodeImages(const string &imagePath, string &outputPath, TranscodeResult &result) +{ + if (!handle_) { + cerr << "Error: handle_ is nullptr."<< endl; + return TranscodeError::LOAD_COMPRESS_FAILED; + } +#ifdef __WIN32 + ITranscodeImages iTranscodeImages = (ITranscodeImages)GetProcAddress(handle_, "Transcode"); +#else + ITranscodeImages iTranscodeImages = (ITranscodeImages)dlsym(handle_, "Transcode"); +#endif + if (!iTranscodeImages) { + cerr << "Error: Failed to get the 'Transcode'."<< endl; + return TranscodeError::LOAD_COMPRESS_FAILED; + } + TranscodeError ret = (*iTranscodeImages)(imagePath, outputPath, result); + if (ret != TranscodeError::SUCCESS) { + auto iter = ERRORCODEMAP.find(ret); + if (iter != ERRORCODEMAP.end()) { + cerr << "Error: TranscodeImages failed, error message: " << iter->second << endl; + } else { + cerr << "Error: TranscodeImages failed." << endl; + } + return ret; + } + return TranscodeError::SUCCESS; +} + +bool CompressionParser::CheckPath(const string &src, const vector &paths) +{ + return any_of(paths.begin(), paths.end(), [src](const auto &iter) { + return iter == src; + }); +} + +bool CompressionParser::IsInPath(const string &src, const shared_ptr &compressFilter) +{ + return CheckPath(src, compressFilter->path); +} + +bool CompressionParser::IsInExcludePath(const string &src, const shared_ptr &compressFilter) +{ + return CheckPath(src, compressFilter->excludePath); +} +string CompressionParser::GetOptionsString(const shared_ptr &compressFilter, int type) +{ + if (compressFilter->rules.size() == 0 || compressFilter->expandRules.size() == 0) { + return "{" + compressFilter->method + "}"; + } + string res = "{" + compressFilter->method + "}"; + switch (type) { + case OPT_TYPE_ONE: + if (compressFilter->rules.size() == OPT_SIZE_ONE) { + res.append(compressFilter->rules[0]).append("}"); + } else { + res.append(compressFilter->rules[0]).append(",").append(compressFilter->rules[1]).append("}"); + } + break; + case OPT_TYPE_TWO: + if (compressFilter->rules.size() == OPT_SIZE_ONE) { + res.append(compressFilter->rules[0]).append("}"); + } else if (compressFilter->expandRules.size() == OPT_SIZE_TWO) { + res.append(compressFilter->rules[0]).append(",").append(compressFilter->expandRules[1]).append("}"); + } + break; + case OPT_TYPE_THREE: + if (compressFilter->expandRules.size() == OPT_SIZE_ONE) { + res.append(compressFilter->expandRules[0]).append("}"); + } else if (compressFilter->rules.size() == OPT_SIZE_TWO) { + res.append(compressFilter->expandRules[0]).append(",").append(compressFilter->rules[1]).append("}"); + } + break; + default: + break; + } + return res; +} + +bool CompressionParser::CopyAndTranscode(const string &src, string &dst) +{ + if (!mediaSwitch_) { + return ResourceUtil::CopyFileInner(src, dst); + } + + auto index = dst.find_last_of(SEPARATOR_FILE); + if (index == string::npos) { + cerr << "Error: invalid output path." << NEW_LINE_PATH << dst << endl; + return false; + } + string output = dst.substr(0, index); + TranscodeResult result = {0, 0, 0, 0, 0}; + for (const auto &compressFilter : compressFilters_) { + if (!IsInPath(src, compressFilter)) { + continue; + } + if (IsInExcludePath(src, compressFilter)) { + if (!SetTranscodeOptions(GetOptionsString(compressFilter, OPT_TYPE_ONE)) || + TranscodeImages(src, output, result) != TranscodeError::SUCCESS) { + continue; + } + dst = output; + return true; + } else { + if ((!SetTranscodeOptions(GetOptionsString(compressFilter, OPT_TYPE_THREE)) || + TranscodeImages(src, output, result) != TranscodeError::SUCCESS) && + (!SetTranscodeOptions(GetOptionsString(compressFilter, OPT_TYPE_TWO)) || + TranscodeImages(src, output, result) != TranscodeError::SUCCESS)) { + continue; + } + dst = output; + return true; + } + } + return ResourceUtil::CopyFileInner(src, dst); +} } } } diff --git a/src/generic_compiler.cpp b/src/generic_compiler.cpp index cfb6904..3798f12 100644 --- a/src/generic_compiler.cpp +++ b/src/generic_compiler.cpp @@ -18,6 +18,7 @@ #include "file_entry.h" #include "resource_util.h" #include "restool_errors.h" +#include "compression_parser.h" namespace OHOS { namespace Global { @@ -37,23 +38,29 @@ uint32_t GenericCompiler::CompileSingleFile(const FileInfo &fileInfo) return RESTOOL_SUCCESS; } - if (!CopyFile(fileInfo)) { + string output = ""; + if (!CopyMediaFile(fileInfo, output)) { return RESTOOL_ERROR; } - if (!PostFile(fileInfo)) { + if (!PostMediaFile(fileInfo, output)) { return RESTOOL_ERROR; } return RESTOOL_SUCCESS; } -bool GenericCompiler::PostFile(const FileInfo &fileInfo) +bool GenericCompiler::PostMediaFile(const FileInfo &fileInfo, const std::string &output) { ResourceItem resourceItem(fileInfo.filename, fileInfo.keyParams, type_); resourceItem.SetFilePath(fileInfo.filePath); resourceItem.SetLimitKey(fileInfo.limitKey); - string data = fileInfo.filename; + auto index = output.find_last_of(SEPARATOR_FILE); + if (index == string::npos) { + cerr << "Error: invalid output path." << NEW_LINE_PATH << output << endl; + return false; + } + string data = output.substr(index + 1); data = moduleName_ + SEPARATOR + RESOURCES_DIR + SEPARATOR + \ fileInfo.limitKey + SEPARATOR + fileInfo.fileCluster + SEPARATOR + data; if (!resourceItem.SetData(reinterpret_cast(data.c_str()), data.length())) { @@ -75,13 +82,14 @@ bool GenericCompiler::IsIgnore(const FileInfo &fileInfo) const return ResourceUtil::FileExist(GetOutputFilePath(fileInfo)); } -bool GenericCompiler::CopyFile(const FileInfo &fileInfo) const +bool GenericCompiler::CopyMediaFile(const FileInfo &fileInfo, std::string &output) { string outputFolder = GetOutputFolder(fileInfo); if (!ResourceUtil::CreateDirs(outputFolder)) { return false; } - return ResourceUtil::CopyFleInner(fileInfo.filePath, GetOutputFilePath(fileInfo)); + output = GetOutputFilePath(fileInfo); + return CompressionParser::GetCompressionParser()->CopyAndTranscode(fileInfo.filePath, output); } } } diff --git a/src/resource_append.cpp b/src/resource_append.cpp index fcc3a7a..fcba409 100644 --- a/src/resource_append.cpp +++ b/src/resource_append.cpp @@ -263,7 +263,7 @@ bool ResourceAppend::ScanFile(const FileInfo &fileInfo, const string &outputPath if (ResourceAppend::IsBaseIdDefined(fileInfo)) { cout << "Warning: id_defined.json does not compile to generate intermediate files" << endl; FileEntry::FilePath outPath(outputPath); - return ResourceUtil::CopyFleInner(fileInfo.filePath, outPath.Append(ID_DEFINED_FILE).GetPath()); + return ResourceUtil::CopyFileInner(fileInfo.filePath, outPath.Append(ID_DEFINED_FILE).GetPath()); } unique_ptr resourceCompiler = @@ -565,7 +565,7 @@ bool ResourceAppend::LoadResourceItemFromMem(const char buffer[], int32_t length continue; } - if (!ResourceUtil::CopyFleInner(filePathStr, outPath.Append(data).GetPath())) { + if (!ResourceUtil::CopyFileInner(filePathStr, outPath.Append(data).GetPath())) { return false; } continue; diff --git a/src/resource_pack.cpp b/src/resource_pack.cpp index 0dab305..f28e4d9 100644 --- a/src/resource_pack.cpp +++ b/src/resource_pack.cpp @@ -304,7 +304,8 @@ uint32_t ResourcePack::CopyRawFileOrResFileImpl(const string &src, const string cerr << "Warning: '" << entry->GetFilePath().GetPath() << "' is defined repeatedly." << endl; continue; } - if (!ResourceUtil::CopyFleInner(entry->GetFilePath().GetPath(), subPath)) { + string path = entry->GetFilePath().GetPath(); + if (!CompressionParser::GetCompressionParser()->CopyAndTranscode(path, subPath)) { return RESTOOL_ERROR; } } @@ -509,7 +510,7 @@ bool ResourcePack::CopyIcon(string &dataPath, const string &idName, string &file cerr << "Error: Create Dirs fail '" << dstDir << "'."<< endl; return false; } - if (!ResourceUtil::CopyFleInner(source, dst)) { + if (!ResourceUtil::CopyFileInner(source, dst)) { cerr << "Error: copy file fail from '" << source << "' to '" << dst << "'." << endl; return false; } diff --git a/src/resource_util.cpp b/src/resource_util.cpp index 29ac939..e4e4d19 100644 --- a/src/resource_util.cpp +++ b/src/resource_util.cpp @@ -209,7 +209,7 @@ ResType ResourceUtil::GetResTypeFromString(const string &type) return ResType::INVALID_RES_TYPE; } -bool ResourceUtil::CopyFleInner(const string &src, const string &dst) +bool ResourceUtil::CopyFileInner(const string &src, const string &dst) { return FileEntry::CopyFileInner(src, dst); }