1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "compression_parser.h"
17 
18 #include <algorithm>
19 #include <iostream>
20 #include <mutex>
21 #include "restool_errors.h"
22 
23 namespace OHOS {
24 namespace Global {
25 namespace Restool {
26 using namespace std;
27 static shared_ptr<CompressionParser> compressionParseMgr = nullptr;
28 static once_flag compressionParserMgrFlag;
29 
30 const map<TranscodeError, string> ERRORCODEMAP = {
31     { TranscodeError::SUCCESS, "SUCCESS" },
32     { TranscodeError::INVALID_PARAMETERS, "INVALID_PARAMETERS" },
33     { TranscodeError::IMAGE_ERROR, "IMAGE_ERROR" },
34     { TranscodeError::ANIMATED_IMAGE_SKIP, "ANIMATED_IMAGE_SKIP" },
35     { TranscodeError::MALLOC_FAILED, "MALLOC_FAILED" },
36     { TranscodeError::ENCODE_ASTC_FAILED, "ENCODE_ASTC_FAILED" },
37     { TranscodeError::SUPER_COMPRESS_FAILED, "SUPER_COMPRESS_FAILED" },
38     { TranscodeError::IMAGE_SIZE_NOT_MATCH, "IMAGE_SIZE_NOT_MATCH" },
39     { TranscodeError::IMAGE_RESOLUTION_NOT_MATCH, "IMAGE_RESOLUTION_NOT_MATCH" },
40     { TranscodeError::EXCLUDE_MATCH, "EXCLUDE_MATCH" },
41     { TranscodeError::LOAD_COMPRESS_FAILED, "LOAD_COMPRESS_FAILED" },
42 };
43 
CompressionParser()44 CompressionParser::CompressionParser()
45     : filePath_(""), extensionPath_(""), mediaSwitch_(false), root_(nullptr), defaultCompress_(false), outPath_("")
46 {
47 }
48 
CompressionParser(const string &filePath)49 CompressionParser::CompressionParser(const string &filePath)
50     : filePath_(filePath), extensionPath_(""), mediaSwitch_(false), root_(nullptr), defaultCompress_(false),
51     outPath_("")
52 {
53 }
54 
~CompressionParser()55 CompressionParser::~CompressionParser()
56 {
57     if (root_) {
58         cJSON_Delete(root_);
59     }
60 #ifdef __WIN32
61     if (handle_) {
62         FreeLibrary(handle_);
63         handle_ = nullptr;
64     }
65 #else
66     if (handle_) {
67         dlclose(handle_);
68         handle_ = nullptr;
69     }
70 #endif
71 }
72 
GetCompressionParser(const string &filePath)73 shared_ptr<CompressionParser> CompressionParser::GetCompressionParser(const string &filePath)
74 {
75     call_once(compressionParserMgrFlag, [&] {
76         compressionParseMgr = make_shared<CompressionParser>(filePath);
77     });
78     return compressionParseMgr;
79 }
80 
GetCompressionParser()81 shared_ptr<CompressionParser> CompressionParser::GetCompressionParser()
82 {
83     if (!compressionParseMgr) {
84         compressionParseMgr = make_shared<CompressionParser>();
85     }
86     return compressionParseMgr;
87 }
88 
Init()89 uint32_t CompressionParser::Init()
90 {
91     if (!ResourceUtil::OpenJsonFile(filePath_, &root_)) {
92         return RESTOOL_ERROR;
93     }
94     if (!root_ || !cJSON_IsObject(root_)) {
95         cerr << "Error: JSON file parsing failed, please check the JSON file." << NEW_LINE_PATH << filePath_ << endl;
96         return RESTOOL_ERROR;
97     }
98     cJSON *contextNode = cJSON_GetObjectItem(root_, "context");
99     if (!ParseContext(contextNode)) {
100         cout << NEW_LINE_PATH << filePath_ << endl;
101         return RESTOOL_SUCCESS;
102     }
103     if (!LoadImageTranscoder()) {
104         return RESTOOL_ERROR;
105     }
106     cJSON *compressionNode = cJSON_GetObjectItem(root_, "compression");
107     if (!ParseCompression(compressionNode)) {
108         return RESTOOL_ERROR;
109     }
110     if (!mediaSwitch_) {
111         return RESTOOL_SUCCESS;
112     }
113     cJSON *filtersNode = cJSON_GetObjectItem(compressionNode, "filters");
114     if (!ParseFilters(filtersNode)) {
115         cerr << NEW_LINE_PATH << filePath_ << endl;
116         return RESTOOL_ERROR;
117     }
118     string caches = outPath_;
119     caches.append(SEPARATOR_FILE).append(CACHES_DIR);
120     if (!ResourceUtil::CreateDirs(caches)) {
121         cerr << "Error: create caches dir failed. dir = " << caches << endl;
122         return RESTOOL_ERROR;
123     }
124     return RESTOOL_SUCCESS;
125 }
126 
ParseCompression(const cJSON *compressionNode)127 bool CompressionParser::ParseCompression(const cJSON *compressionNode)
128 {
129     if (!compressionNode) {
130         cerr << "Warning: get 'compression' node is empty, the compiled images are not transcoded.";
131         cerr << NEW_LINE_PATH << filePath_ << endl;
132         return true;
133     }
134     if (!cJSON_IsObject(compressionNode)) {
135         cerr << "Error: 'compression' must be object." << NEW_LINE_PATH << filePath_ << endl;
136         return false;
137     }
138     cJSON *mediaNode = cJSON_GetObjectItem(compressionNode, "media");
139     if (!mediaNode) {
140         cerr << "Warning: get 'media' node is empty, the compiled images are not transcoded.";
141         cerr << NEW_LINE_PATH << filePath_ << endl;
142         return true;
143     }
144     if (!cJSON_IsObject(mediaNode)) {
145         cerr << "Error: 'media' must be object." << NEW_LINE_PATH << filePath_ << endl;
146         return false;
147     }
148     cJSON *enableNode = cJSON_GetObjectItem(mediaNode, "enable");
149     if (!enableNode) {
150         cerr << "Warning: get 'enable' node is empty, the compiled images are not transcoded.";
151         cerr << NEW_LINE_PATH << filePath_ << endl;
152         return true;
153     }
154     if (!cJSON_IsBool(enableNode)) {
155         cerr << "Error: 'enable' must be bool." << NEW_LINE_PATH << filePath_ << endl;
156         return false;
157     }
158     mediaSwitch_ = cJSON_IsTrue(enableNode);
159     return true;
160 }
161 
ParseContext(const cJSON *contextNode)162 bool CompressionParser::ParseContext(const cJSON *contextNode)
163 {
164     if (!contextNode) {
165         cout << "Warning: if image transcoding is supported, the 'context' node cannot be empty.";
166         return false;
167     }
168     if (!cJSON_IsObject(contextNode)) {
169         cout << "Warning: 'context' must be object.";
170         return false;
171     }
172     cJSON *extensionPathNode = cJSON_GetObjectItem(contextNode, "extensionPath");
173     if (!extensionPathNode) {
174         cout << "Warning: if image transcoding is supported, the 'extensionPath' node cannot be empty.";
175         return false;
176     }
177     if (!cJSON_IsString(extensionPathNode)) {
178         cout << "Warning: 'extensionPath' must be string.";
179         return false;
180     }
181     extensionPath_ = extensionPathNode->valuestring;
182     if (extensionPath_.empty()) {
183         cout << "Warning: 'extensionPath' value cannot be empty.";
184         return false;
185     }
186     return true;
187 }
188 
ParseFilters(const cJSON *filtersNode)189 bool CompressionParser::ParseFilters(const cJSON *filtersNode)
190 {
191     if (!filtersNode) {
192         cerr << "Error: if image transcoding is supported, the 'filters' node cannot be empty.";
193         return false;
194     }
195     if (!cJSON_IsArray(filtersNode)) {
196         cerr << "Error: 'filters' must be array.";
197         return false;
198     }
199     if (cJSON_GetArraySize(filtersNode) == 0) {
200         cerr << "Error: 'filters' value cannot be empty.";
201         return false;
202     }
203     for (cJSON *item = filtersNode->child; item; item = item->next) {
204         if (!cJSON_IsObject(item)) {
205             cerr << "Error: 'filters' value type must be object.";
206             return false;
207         }
208         cJSON *methodNode = cJSON_GetObjectItem(item, "method");
209         if (!methodNode) {
210             cerr << "Error: if image transcoding is supported, the 'method' node cannot be empty.";
211             return false;
212         }
213         if (!cJSON_IsObject(methodNode)) {
214             cerr << "Error: 'method' must be object.";
215             return false;
216         }
217         shared_ptr<CompressFilter> compressFilter = make_shared<CompressFilter>();
218         compressFilter->method = "\"method\":" + ParseJsonStr(methodNode);
219         cJSON *pathNode = cJSON_GetObjectItem(item, "path");
220         compressFilter->path = ParsePath(pathNode);
221         cJSON *excludePathNode = cJSON_GetObjectItem(item, "exclude_path");
222         compressFilter->excludePath = ParsePath(excludePathNode);
223         cJSON *rulesNode = cJSON_GetObjectItem(item, "rules_origin");
224         compressFilter->rules = ParseRules(rulesNode);
225         cJSON *excludeRulesNode = cJSON_GetObjectItem(item, "rules_exclude");
226         compressFilter->excludeRules = ParseRules(excludeRulesNode);
227         compressFilters_.emplace_back(compressFilter);
228     }
229     defaultCompress_ = IsDefaultCompress();
230     return true;
231 }
232 
IsDefaultCompress()233 bool CompressionParser::IsDefaultCompress()
234 {
235     if (compressFilters_.size() != 1) {
236         return false;
237     }
238     auto compressFilter = compressFilters_[0];
239     bool pathEmpty = (compressFilter->path.size() == 1) && (compressFilter->path[0] == "true");
240     bool excludePathEmpty = (compressFilter->excludePath.size() == 1) && (compressFilter->excludePath[0] == "true");
241     return pathEmpty && excludePathEmpty && (compressFilter->rules.empty()) && (compressFilter->excludeRules.empty());
242 }
243 
SetOutPath(const string &path)244 void CompressionParser::SetOutPath(const string &path)
245 {
246     outPath_ = path;
247 }
248 
ParseRules(const cJSON *rulesNode)249 string CompressionParser::ParseRules(const cJSON *rulesNode)
250 {
251     string res = "";
252     if (!rulesNode || !cJSON_IsObject(rulesNode)) {
253         cout << "Warning: rules is not exist or node type is wrong" << endl;
254         return res;
255     }
256     for (cJSON *item = rulesNode->child; item; item = item->next) {
257         if (!item || !cJSON_IsArray(item)) {
258             continue;
259         }
260         string name(item->string);
261         res.append("\"").append(name).append("\":").append(ParseJsonStr(item)).append(",");
262     }
263     if (res.size() - 1 < 0) {
264         return res;
265     }
266     return res.substr(0, res.size() - 1);
267 }
268 
ParsePath(const cJSON *pathNode)269 vector<string> CompressionParser::ParsePath(const cJSON *pathNode)
270 {
271     vector<string> res;
272     if (!pathNode) {
273         res.emplace_back("true");
274         return res;
275     }
276     if (!cJSON_IsArray(pathNode)) {
277         cout << "Warning: pathnode is not array." << endl;
278         return res;
279     }
280     for (cJSON *item = pathNode->child; item; item = item->next) {
281         if (!item || !cJSON_IsString(item)) {
282             continue;
283         }
284         res.emplace_back(item->valuestring);
285     }
286     return res;
287 }
288 
ParseJsonStr(const cJSON *node)289 string CompressionParser::ParseJsonStr(const cJSON *node)
290 {
291     if (!node) {
292         return "";
293     }
294     char *jsonString = cJSON_Print(node);
295     string res(jsonString);
296     free(jsonString);
297     return res;
298 }
299 
LoadImageTranscoder()300 bool CompressionParser::LoadImageTranscoder()
301 {
302 #ifdef __WIN32
303     if (!handle_) {
304         handle_ = LoadLibrary(TEXT(extensionPath_.c_str()));
305         if (!handle_) {
306             cerr << "Error: open '" << extensionPath_.c_str() << "' fail." << endl;
307             cerr << "Error: LoadLibrary failed with error: " << GetLastError() << endl;
308             return false;
309         }
310     }
311 #else
312     if (!handle_) {
313         string realPath = ResourceUtil::RealPath(extensionPath_);
314         if (realPath.empty()) {
315             cerr << "Error: open '" << extensionPath_.c_str() << "' fail, real path empty." << endl;
316             return false;
317         }
318         handle_ = dlopen(realPath.c_str(), RTLD_LAZY);
319         if (!handle_) {
320             cerr << "Error: open '" << realPath.c_str() << "' fail." << endl;
321             cerr << "Error: dlopen failed with error: " << dlerror() << endl;
322             return false;
323         }
324     }
325 #endif
326     return true;
327 }
328 
SetTranscodeOptions(const string &optionJson, const string &optionJsonExclude)329 bool CompressionParser::SetTranscodeOptions(const string &optionJson, const string &optionJsonExclude)
330 {
331     if (!handle_) {
332         cout << "Warning: SetTranscodeOptions handle_ is nullptr." << endl;
333         return false;
334     }
335 #ifdef __WIN32
336     ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)GetProcAddress(handle_, "SetTranscodeOptions");
337 #else
338     ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)dlsym(handle_, "SetTranscodeOptions");
339 #endif
340     if (!iSetTranscodeOptions) {
341         cout << "Warning: Failed to get the 'SetTranscodeOptions'." << endl;
342         return false;
343     }
344     bool ret = (*iSetTranscodeOptions)(optionJson, optionJsonExclude);
345     if (!ret) {
346         cout << "Warning: SetTranscodeOptions failed." << endl;
347         return false;
348     }
349     return true;
350 }
351 
TranscodeImages(const string &imagePath, const bool extAppend, string &outputPath, TranscodeResult &result)352 TranscodeError CompressionParser::TranscodeImages(const string &imagePath, const bool extAppend,
353     string &outputPath, TranscodeResult &result)
354 {
355     if (!handle_) {
356         cout << "Warning: TranscodeImages handle_ is nullptr." << endl;
357         return TranscodeError::LOAD_COMPRESS_FAILED;
358     }
359 #ifdef __WIN32
360     ITranscodeImages iTranscodeImages = (ITranscodeImages)GetProcAddress(handle_, "Transcode");
361 #else
362     ITranscodeImages iTranscodeImages = (ITranscodeImages)dlsym(handle_, "Transcode");
363 #endif
364     if (!iTranscodeImages) {
365         cout << "Warning: Failed to get the 'Transcode'." << endl;
366         return TranscodeError::LOAD_COMPRESS_FAILED;
367     }
368     TranscodeError ret = (*iTranscodeImages)(imagePath, extAppend, outputPath, result);
369     if (ret != TranscodeError::SUCCESS) {
370         auto iter = ERRORCODEMAP.find(ret);
371         if (iter != ERRORCODEMAP.end()) {
372             cout << "Warning: TranscodeImages failed, error message: " << iter->second << ", file path = " <<
373                 imagePath << endl;
374         } else {
375             cout << "Warning: TranscodeImages failed" << ", file path = " << imagePath << endl;
376         }
377         return ret;
378     }
379     return TranscodeError::SUCCESS;
380 }
381 
ScaleImage(const std::string &imagePath, std::string &outputPath)382 TranscodeError CompressionParser::ScaleImage(const std::string &imagePath, std::string &outputPath)
383 {
384     if (!handle_) {
385         cout << "Warning: ScaleImage handle_ is nullptr." << endl;
386         return TranscodeError::LOAD_COMPRESS_FAILED;
387     }
388 #ifdef __WIN32
389     IScaleImage iScaleImage = (IScaleImage)GetProcAddress(handle_, "TranscodeSLR");
390 #else
391     IScaleImage iScaleImage = (IScaleImage)dlsym(handle_, "TranscodeSLR");
392 #endif
393     if (!iScaleImage) {
394         cout << "Warning: Failed to get the 'TranscodeSLR'." << endl;
395         return TranscodeError::LOAD_COMPRESS_FAILED;
396     }
397     TranscodeError ret = (*iScaleImage)(imagePath, outputPath, { 512, 512 });
398     if (ret != TranscodeError::SUCCESS) {
399         auto iter = ERRORCODEMAP.find(ret);
400         if (iter != ERRORCODEMAP.end()) {
401             cout << "Warning: ScaleImage failed, error message: " << iter->second << ", file path = " << imagePath
402                  << endl;
403         } else {
404             cout << "Warning: ScaleImage failed" << ", file path = " << imagePath << endl;
405         }
406         return ret;
407     }
408     return TranscodeError::SUCCESS;
409 }
410 
CheckPath(const string &src, const vector<string> &paths)411 bool CompressionParser::CheckPath(const string &src, const vector<string> &paths)
412 {
413     if (paths.size() == 1 && paths[0] == "true") {
414         return true;
415     }
416     return any_of(paths.begin(), paths.end(), [src](const auto &iter) {
417         return iter == src;
418     });
419 }
420 
IsInPath(const string &src, const shared_ptr<CompressFilter> &compressFilter)421 bool CompressionParser::IsInPath(const string &src, const shared_ptr<CompressFilter> &compressFilter)
422 {
423     return CheckPath(src, compressFilter->path);
424 }
425 
IsInExcludePath(const string &src, const shared_ptr<CompressFilter> &compressFilter)426 bool CompressionParser::IsInExcludePath(const string &src, const shared_ptr<CompressFilter> &compressFilter)
427 {
428     return CheckPath(src, compressFilter->excludePath);
429 }
430 
GetMethod(const shared_ptr<CompressFilter> &compressFilter)431 string CompressionParser::GetMethod(const shared_ptr<CompressFilter> &compressFilter)
432 {
433     return "{" + compressFilter->method + "}";
434 }
435 
GetRules(const shared_ptr<CompressFilter> &compressFilter)436 string CompressionParser::GetRules(const shared_ptr<CompressFilter> &compressFilter)
437 {
438     return GetFileRules(compressFilter->rules, compressFilter->method);
439 }
440 
GetExcludeRules(const shared_ptr<CompressFilter> &compressFilter)441 string CompressionParser::GetExcludeRules(const shared_ptr<CompressFilter> &compressFilter)
442 {
443     return GetFileRules(compressFilter->excludeRules, compressFilter->method);
444 }
445 
GetFileRules(const string &rules, const string &method)446 string CompressionParser::GetFileRules(const string &rules, const string &method)
447 {
448     if (rules.empty()) {
449         return "{" + method + "}";
450     }
451     string res = "{";
452     return res.append(method).append(",").append(rules).append("}");
453 }
454 
CollectTime(uint32_t &count, unsigned long long &time, std::chrono::time_point<std::chrono::steady_clock> &start)455 void CompressionParser::CollectTime(uint32_t &count, unsigned long long &time,
456     std::chrono::time_point<std::chrono::steady_clock> &start)
457 {
458     unsigned long long costTime = static_cast<unsigned long long>(
459         std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
460     time += costTime;
461     count++;
462 }
463 
CollectTimeAndSize(TranscodeError res, std::chrono::time_point<std::chrono::steady_clock> &start, TranscodeResult &result)464 void CompressionParser::CollectTimeAndSize(TranscodeError res,
465     std::chrono::time_point<std::chrono::steady_clock> &start, TranscodeResult &result)
466 {
467     unsigned long long costTime = static_cast<unsigned long long>(
468         std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
469     if (res == TranscodeError::SUCCESS) {
470         totalTime_ += costTime;
471         totalCounts_++;
472         compressTime_ += costTime;
473         compressCounts_++;
474         successTime_ += costTime;
475         successCounts_++;
476         originalSize_ += result.originSize;
477         successSize_ += static_cast<unsigned long long>(result.size);
478     } else if (res < TranscodeError::NOT_MATCH_BASE) {
479         totalTime_ += costTime;
480         compressTime_ += costTime;
481         compressCounts_++;
482     } else {
483         totalTime_ += costTime;
484     }
485 }
486 
PrintTransMessage()487 string CompressionParser::PrintTransMessage()
488 {
489     string res = "Processing report:\n";
490     res.append("total:").append(to_string(totalCounts_)).append(", ").append(to_string(totalTime_)).append(" us.\n");
491     res.append("compressed:").append(to_string(compressCounts_)).append(", ").append(to_string(compressTime_))
492         .append(" us.\n");
493     res.append("success:").append(to_string(successCounts_)).append(", ").append(to_string(successTime_))
494         .append(" us, ").append(to_string(originalSize_)).append(" Bytes to ").append(to_string(successSize_))
495         .append(" Bytes.");
496     return res;
497 }
498 
GetMediaSwitch()499 bool CompressionParser::GetMediaSwitch()
500 {
501     return mediaSwitch_;
502 }
503 
GetDefaultCompress()504 bool CompressionParser::GetDefaultCompress()
505 {
506     return defaultCompress_;
507 }
508 
CheckAndTranscode(const string &src, string &dst, string &output, const shared_ptr<CompressFilter> &compressFilter, const bool extAppend)509 bool CompressionParser::CheckAndTranscode(const string &src, string &dst, string &output,
510     const shared_ptr<CompressFilter> &compressFilter, const bool extAppend)
511 {
512     auto t1 = std::chrono::steady_clock::now();
513     TranscodeResult result = {0, 0, 0, 0};
514     if (defaultCompress_) {
515         if (!SetTranscodeOptions(GetMethod(compressFilter), "")) {
516             return false;
517         }
518         auto res = TranscodeImages(src, extAppend, output, result);
519         CollectTimeAndSize(res, t1, result);
520         if (res != TranscodeError::SUCCESS) {
521             return false;
522         }
523         dst = output;
524         return true;
525     }
526     if (!IsInPath(src, compressFilter)) {
527         return false;
528     }
529     if (IsInExcludePath(src, compressFilter)) {
530         if (!SetTranscodeOptions(GetRules(compressFilter), GetExcludeRules(compressFilter))) {
531             return false;
532         }
533         auto res = TranscodeImages(src, extAppend, output, result);
534         CollectTimeAndSize(res, t1, result);
535         if (res != TranscodeError::SUCCESS) {
536             return false;
537         }
538         dst = output;
539         return true;
540     }
541     if (!SetTranscodeOptions(GetRules(compressFilter), "")) {
542         return false;
543     }
544     auto res = TranscodeImages(src, extAppend, output, result);
545     CollectTimeAndSize(res, t1, result);
546     if (res != TranscodeError::SUCCESS) {
547         return false;
548     }
549     dst = output;
550     return true;
551 }
552 
CopyForTrans(const string &src, const string &originDst, const string &dst)553 bool CompressionParser::CopyForTrans(const string &src, const string &originDst, const string &dst)
554 {
555     string srcSuffix;
556     string dstSuffix;
557     auto srcIndex = src.find_last_of(".");
558     auto dstIndex = dst.find_last_of(".");
559     if (srcIndex != string::npos && dstIndex != string::npos) {
560         srcSuffix = src.substr(srcIndex + 1);
561         dstSuffix = dst.substr(dstIndex + 1);
562     }
563     auto ret = false;
564     if (srcSuffix == dstSuffix) {
565         ret = ResourceUtil::CopyFileInner(src, dst);
566     } else {
567         uint32_t startIndex = outPath_.size() + CACHES_DIR.size() + 1;
568         string dstPath = outPath_ + SEPARATOR_FILE + RESOURCES_DIR + dst.substr(startIndex);
569         ret = ResourceUtil::CopyFileInner(dst, dstPath);
570     }
571     return ret;
572 }
573 
CopyAndTranscode(const string &src, string &dst, const bool extAppend)574 bool CompressionParser::CopyAndTranscode(const string &src, string &dst, const bool extAppend)
575 {
576     auto t0 = std::chrono::steady_clock::now();
577     if (!mediaSwitch_) {
578         auto res = ResourceUtil::CopyFileInner(src, dst);
579         CollectTime(totalCounts_, totalTime_, t0);
580         return res;
581     }
582 
583     auto index = dst.find_last_of(SEPARATOR_FILE);
584     if (index == string::npos) {
585         cerr << "Error: invalid output path." << NEW_LINE_PATH << dst << endl;
586         return false;
587     }
588     uint32_t startIndex = outPath_.size() + RESOURCES_DIR.size() + 1;
589     string endStr = dst.substr(startIndex, index - startIndex);
590     string output = outPath_ + SEPARATOR_FILE + CACHES_DIR + endStr;
591     string originDst = dst;
592     if (!ResourceUtil::CreateDirs(output)) {
593         cerr << "Error: create output dir failed. dir = " << output << endl;
594         return false;
595     }
596     for (const auto &compressFilter : compressFilters_) {
597         if (!CheckAndTranscode(src, dst, output, compressFilter, extAppend)) {
598             continue;
599         }
600         break;
601     }
602     auto t2 = std::chrono::steady_clock::now();
603     auto ret = CopyForTrans(src, originDst, dst);
604     CollectTime(totalCounts_, totalTime_, t2);
605     return ret;
606 }
607 
CheckAndScaleIcon(const std::string &src, const std::string &originDst, std::string &scaleDst)608 bool CompressionParser::CheckAndScaleIcon(const std::string &src, const std::string &originDst, std::string &scaleDst)
609 {
610     scaleDst = src;
611     if (filePath_.empty() || outPath_.empty()) {
612         cout << "Info: compression path or out path is empty, unable to scale icon." << endl;
613         return true;
614     }
615     auto index = originDst.find_last_of(SEPARATOR_FILE);
616     if (index == string::npos) {
617         cerr << "Error: invalid output path." << NEW_LINE_PATH << originDst << endl;
618         return false;
619     }
620     uint32_t startIndex = outPath_.size() + RESOURCES_DIR.size() + 1;
621     string qualifierDir = originDst.substr(startIndex, index - startIndex);
622     string outputCache = outPath_ + SEPARATOR_FILE + CACHES_DIR + qualifierDir;
623     if (!ResourceUtil::CreateDirs(outputCache)) {
624         cerr << "Error: scale icon create output dir failed. dir = " << outputCache << endl;
625         return false;
626     }
627     string fileName = originDst.substr(index + 1);
628     string outputFile = outputCache + SEPARATOR_FILE + fileName;
629     auto ret = ScaleImage(src, outputFile);
630     if (ret == TranscodeError::SUCCESS) {
631         // if scale success, change src file to scale image
632         scaleDst = outputFile;
633     }
634     return true;
635 }
636 
ScaleIconEnable()637 bool CompressionParser::ScaleIconEnable()
638 {
639     return !filePath_.empty() && !outPath_.empty() && handle_ != nullptr;
640 }
641 }
642 }
643 }
644