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 PrintError(GetError(ERR_CODE_JSON_FORMAT_ERROR).SetPosition(filePath_));
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 return RESTOOL_ERROR;
116 }
117 string caches = outPath_;
118 caches.append(SEPARATOR_FILE).append(CACHES_DIR);
119 if (!ResourceUtil::CreateDirs(caches)) {
120 return RESTOOL_ERROR;
121 }
122 return RESTOOL_SUCCESS;
123 }
124
ParseCompression(const cJSON * compressionNode)125 bool CompressionParser::ParseCompression(const cJSON *compressionNode)
126 {
127 if (!compressionNode) {
128 cout << "Warning: get 'compression' node is empty, the compiled images are not transcoded.";
129 cout << NEW_LINE_PATH << filePath_ << endl;
130 return true;
131 }
132 if (!cJSON_IsObject(compressionNode)) {
133 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("compression", "object").SetPosition(filePath_));
134 return false;
135 }
136 cJSON *mediaNode = cJSON_GetObjectItem(compressionNode, "media");
137 if (!mediaNode) {
138 cout << "Warning: get 'media' node is empty, the compiled images are not transcoded.";
139 cout << NEW_LINE_PATH << filePath_ << endl;
140 return true;
141 }
142 if (!cJSON_IsObject(mediaNode)) {
143 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("media", "object").SetPosition(filePath_));
144 return false;
145 }
146 cJSON *enableNode = cJSON_GetObjectItem(mediaNode, "enable");
147 if (!enableNode) {
148 cout << "Warning: get 'enable' node is empty, the compiled images are not transcoded.";
149 cout << NEW_LINE_PATH << filePath_ << endl;
150 return true;
151 }
152 if (!cJSON_IsBool(enableNode)) {
153 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("enable", "bool").SetPosition(filePath_));
154 return false;
155 }
156 mediaSwitch_ = cJSON_IsTrue(enableNode);
157 return true;
158 }
159
ParseContext(const cJSON * contextNode)160 bool CompressionParser::ParseContext(const cJSON *contextNode)
161 {
162 if (!contextNode) {
163 cout << "Warning: if image transcoding is supported, the 'context' node cannot be empty.";
164 return false;
165 }
166 if (!cJSON_IsObject(contextNode)) {
167 cout << "Warning: 'context' must be object.";
168 return false;
169 }
170 cJSON *extensionPathNode = cJSON_GetObjectItem(contextNode, "extensionPath");
171 if (!extensionPathNode) {
172 cout << "Warning: if image transcoding is supported, the 'extensionPath' node cannot be empty.";
173 return false;
174 }
175 if (!cJSON_IsString(extensionPathNode)) {
176 cout << "Warning: 'extensionPath' must be string.";
177 return false;
178 }
179 extensionPath_ = extensionPathNode->valuestring;
180 if (extensionPath_.empty()) {
181 cout << "Warning: 'extensionPath' value cannot be empty.";
182 return false;
183 }
184 return true;
185 }
186
ParseFilters(const cJSON * filtersNode)187 bool CompressionParser::ParseFilters(const cJSON *filtersNode)
188 {
189 if (!filtersNode) {
190 PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("filters").SetPosition(filePath_));
191 return false;
192 }
193 if (!cJSON_IsArray(filtersNode)) {
194 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("filters", "array").SetPosition(filePath_));
195 return false;
196 }
197 if (cJSON_GetArraySize(filtersNode) == 0) {
198 PrintError(GetError(ERR_CODE_JSON_NODE_EMPTY).FormatCause("filters").SetPosition(filePath_));
199 return false;
200 }
201 for (cJSON *item = filtersNode->child; item; item = item->next) {
202 if (!cJSON_IsObject(item)) {
203 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("filters's value", "object")
204 .SetPosition(filePath_));
205 return false;
206 }
207 cJSON *methodNode = cJSON_GetObjectItem(item, "method");
208 if (!methodNode) {
209 PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("method").SetPosition(filePath_));
210 return false;
211 }
212 if (!cJSON_IsObject(methodNode)) {
213 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("method", "object").SetPosition(filePath_));
214 return false;
215 }
216 shared_ptr<CompressFilter> compressFilter = make_shared<CompressFilter>();
217 compressFilter->method = "\"method\":" + ParseJsonStr(methodNode);
218 cJSON *pathNode = cJSON_GetObjectItem(item, "path");
219 compressFilter->path = ParsePath(pathNode);
220 cJSON *excludePathNode = cJSON_GetObjectItem(item, "exclude_path");
221 compressFilter->excludePath = ParsePath(excludePathNode);
222 cJSON *rulesNode = cJSON_GetObjectItem(item, "rules_origin");
223 compressFilter->rules = ParseRules(rulesNode);
224 cJSON *excludeRulesNode = cJSON_GetObjectItem(item, "rules_exclude");
225 compressFilter->excludeRules = ParseRules(excludeRulesNode);
226 compressFilters_.emplace_back(compressFilter);
227 }
228 defaultCompress_ = IsDefaultCompress();
229 return true;
230 }
231
IsDefaultCompress()232 bool CompressionParser::IsDefaultCompress()
233 {
234 if (compressFilters_.size() != 1) {
235 return false;
236 }
237 auto compressFilter = compressFilters_[0];
238 bool pathEmpty = (compressFilter->path.size() == 1) && (compressFilter->path[0] == "true");
239 bool excludePathEmpty = (compressFilter->excludePath.size() == 1) && (compressFilter->excludePath[0] == "true");
240 return pathEmpty && excludePathEmpty && (compressFilter->rules.empty()) && (compressFilter->excludeRules.empty());
241 }
242
SetOutPath(const string & path)243 void CompressionParser::SetOutPath(const string &path)
244 {
245 outPath_ = path;
246 }
247
ParseRules(const cJSON * rulesNode)248 string CompressionParser::ParseRules(const cJSON *rulesNode)
249 {
250 string res = "";
251 if (!rulesNode || !cJSON_IsObject(rulesNode)) {
252 cout << "Warning: rules is not exist or node type is wrong" << endl;
253 return res;
254 }
255 for (cJSON *item = rulesNode->child; item; item = item->next) {
256 if (!item || !cJSON_IsArray(item)) {
257 continue;
258 }
259 string name(item->string);
260 res.append("\"").append(name).append("\":").append(ParseJsonStr(item)).append(",");
261 }
262 if (res.size() - 1 < 0) {
263 return res;
264 }
265 return res.substr(0, res.size() - 1);
266 }
267
ParsePath(const cJSON * pathNode)268 vector<string> CompressionParser::ParsePath(const cJSON *pathNode)
269 {
270 vector<string> res;
271 if (!pathNode) {
272 res.emplace_back("true");
273 return res;
274 }
275 if (!cJSON_IsArray(pathNode)) {
276 cout << "Warning: pathnode is not array." << endl;
277 return res;
278 }
279 for (cJSON *item = pathNode->child; item; item = item->next) {
280 if (!item || !cJSON_IsString(item)) {
281 continue;
282 }
283 res.emplace_back(item->valuestring);
284 }
285 return res;
286 }
287
ParseJsonStr(const cJSON * node)288 string CompressionParser::ParseJsonStr(const cJSON *node)
289 {
290 if (!node) {
291 return "";
292 }
293 char *jsonString = cJSON_Print(node);
294 if (jsonString == nullptr) {
295 return "";
296 }
297 string res(jsonString);
298 free(jsonString);
299 return res;
300 }
301
LoadImageTranscoder()302 bool CompressionParser::LoadImageTranscoder()
303 {
304 #ifdef __WIN32
305 if (!handle_) {
306 handle_ = LoadLibrary(TEXT(extensionPath_.c_str()));
307 if (!handle_) {
308 DWORD err = GetLastError();
309 LPVOID lpMsgBuf;
310 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
311 NULL, err, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
312 const char *msg = static_cast<const char *>(lpMsgBuf);
313 std::string errMsg = "(" + std::to_string(err) + ")" + string(msg);
314 PrintError(GetError(ERR_CODE_LOAD_LIBRARY_FAIL).FormatCause(extensionPath_.c_str(), errMsg.c_str()));
315 LocalFree(lpMsgBuf);
316 return false;
317 }
318 }
319 #else
320 if (!handle_) {
321 string realPath = ResourceUtil::RealPath(extensionPath_);
322 if (realPath.empty()) {
323 PrintError(GetError(ERR_CODE_LOAD_LIBRARY_FAIL).FormatCause(extensionPath_.c_str(), "path invalid"));
324 return false;
325 }
326 handle_ = dlopen(realPath.c_str(), RTLD_LAZY);
327 if (!handle_) {
328 char *err = dlerror();
329 PrintError(GetError(ERR_CODE_LOAD_LIBRARY_FAIL).FormatCause(realPath.c_str(), err));
330 return false;
331 }
332 }
333 #endif
334 return true;
335 }
336
SetTranscodeOptions(const string & optionJson,const string & optionJsonExclude)337 bool CompressionParser::SetTranscodeOptions(const string &optionJson, const string &optionJsonExclude)
338 {
339 if (!handle_) {
340 cout << "Warning: SetTranscodeOptions handle_ is nullptr." << endl;
341 return false;
342 }
343 #ifdef __WIN32
344 ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)GetProcAddress(handle_, "SetTranscodeOptions");
345 #else
346 ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)dlsym(handle_, "SetTranscodeOptions");
347 #endif
348 if (!iSetTranscodeOptions) {
349 cout << "Warning: Failed to get the 'SetTranscodeOptions'." << endl;
350 return false;
351 }
352 bool ret = (*iSetTranscodeOptions)(optionJson, optionJsonExclude);
353 if (!ret) {
354 cout << "Warning: SetTranscodeOptions failed." << endl;
355 return false;
356 }
357 return true;
358 }
359
TranscodeImages(const string & imagePath,const bool extAppend,string & outputPath,TranscodeResult & result)360 TranscodeError CompressionParser::TranscodeImages(const string &imagePath, const bool extAppend,
361 string &outputPath, TranscodeResult &result)
362 {
363 if (!handle_) {
364 cout << "Warning: TranscodeImages handle_ is nullptr." << endl;
365 return TranscodeError::LOAD_COMPRESS_FAILED;
366 }
367 #ifdef __WIN32
368 ITranscodeImages iTranscodeImages = (ITranscodeImages)GetProcAddress(handle_, "Transcode");
369 #else
370 ITranscodeImages iTranscodeImages = (ITranscodeImages)dlsym(handle_, "Transcode");
371 #endif
372 if (!iTranscodeImages) {
373 cout << "Warning: Failed to get the 'Transcode'." << endl;
374 return TranscodeError::LOAD_COMPRESS_FAILED;
375 }
376 TranscodeError ret = (*iTranscodeImages)(imagePath, extAppend, outputPath, result);
377 if (ret != TranscodeError::SUCCESS) {
378 auto iter = ERRORCODEMAP.find(ret);
379 if (iter != ERRORCODEMAP.end()) {
380 cout << "Warning: TranscodeImages failed, error message: " << iter->second << ", file path = " <<
381 imagePath << endl;
382 } else {
383 cout << "Warning: TranscodeImages failed" << ", file path = " << imagePath << endl;
384 }
385 return ret;
386 }
387 return TranscodeError::SUCCESS;
388 }
389
ScaleImage(const std::string & imagePath,std::string & outputPath)390 TranscodeError CompressionParser::ScaleImage(const std::string &imagePath, std::string &outputPath)
391 {
392 if (!handle_) {
393 cout << "Warning: ScaleImage handle_ is nullptr." << endl;
394 return TranscodeError::LOAD_COMPRESS_FAILED;
395 }
396 #ifdef __WIN32
397 IScaleImage iScaleImage = (IScaleImage)GetProcAddress(handle_, "TranscodeSLR");
398 #else
399 IScaleImage iScaleImage = (IScaleImage)dlsym(handle_, "TranscodeSLR");
400 #endif
401 if (!iScaleImage) {
402 cout << "Warning: Failed to get the 'TranscodeSLR'." << endl;
403 return TranscodeError::LOAD_COMPRESS_FAILED;
404 }
405 TranscodeError ret = (*iScaleImage)(imagePath, outputPath, { 512, 512 });
406 if (ret != TranscodeError::SUCCESS) {
407 auto iter = ERRORCODEMAP.find(ret);
408 if (iter != ERRORCODEMAP.end()) {
409 cout << "Warning: ScaleImage failed, error message: " << iter->second << ", file path = " << imagePath
410 << endl;
411 } else {
412 cout << "Warning: ScaleImage failed" << ", file path = " << imagePath << endl;
413 }
414 return ret;
415 }
416 return TranscodeError::SUCCESS;
417 }
418
CheckPath(const string & src,const vector<string> & paths)419 bool CompressionParser::CheckPath(const string &src, const vector<string> &paths)
420 {
421 if (paths.size() == 1 && paths[0] == "true") {
422 return true;
423 }
424 return any_of(paths.begin(), paths.end(), [src](const auto &iter) {
425 return iter == src;
426 });
427 }
428
IsInPath(const string & src,const shared_ptr<CompressFilter> & compressFilter)429 bool CompressionParser::IsInPath(const string &src, const shared_ptr<CompressFilter> &compressFilter)
430 {
431 return CheckPath(src, compressFilter->path);
432 }
433
IsInExcludePath(const string & src,const shared_ptr<CompressFilter> & compressFilter)434 bool CompressionParser::IsInExcludePath(const string &src, const shared_ptr<CompressFilter> &compressFilter)
435 {
436 return CheckPath(src, compressFilter->excludePath);
437 }
438
GetMethod(const shared_ptr<CompressFilter> & compressFilter)439 string CompressionParser::GetMethod(const shared_ptr<CompressFilter> &compressFilter)
440 {
441 return "{" + compressFilter->method + "}";
442 }
443
GetRules(const shared_ptr<CompressFilter> & compressFilter)444 string CompressionParser::GetRules(const shared_ptr<CompressFilter> &compressFilter)
445 {
446 return GetFileRules(compressFilter->rules, compressFilter->method);
447 }
448
GetExcludeRules(const shared_ptr<CompressFilter> & compressFilter)449 string CompressionParser::GetExcludeRules(const shared_ptr<CompressFilter> &compressFilter)
450 {
451 return GetFileRules(compressFilter->excludeRules, compressFilter->method);
452 }
453
GetFileRules(const string & rules,const string & method)454 string CompressionParser::GetFileRules(const string &rules, const string &method)
455 {
456 if (rules.empty()) {
457 return "{" + method + "}";
458 }
459 string res = "{";
460 return res.append(method).append(",").append(rules).append("}");
461 }
462
CollectTime(uint32_t & count,unsigned long long & time,std::chrono::time_point<std::chrono::steady_clock> & start)463 void CompressionParser::CollectTime(uint32_t &count, unsigned long long &time,
464 std::chrono::time_point<std::chrono::steady_clock> &start)
465 {
466 unsigned long long costTime = static_cast<unsigned long long>(
467 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
468 time += costTime;
469 count++;
470 }
471
CollectTimeAndSize(TranscodeError res,std::chrono::time_point<std::chrono::steady_clock> & start,TranscodeResult & result)472 void CompressionParser::CollectTimeAndSize(TranscodeError res,
473 std::chrono::time_point<std::chrono::steady_clock> &start, TranscodeResult &result)
474 {
475 unsigned long long costTime = static_cast<unsigned long long>(
476 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
477 if (res == TranscodeError::SUCCESS) {
478 totalTime_ += costTime;
479 totalCounts_++;
480 compressTime_ += costTime;
481 compressCounts_++;
482 successTime_ += costTime;
483 successCounts_++;
484 originalSize_ += result.originSize;
485 successSize_ += static_cast<unsigned long long>(result.size);
486 } else if (res < TranscodeError::NOT_MATCH_BASE) {
487 totalTime_ += costTime;
488 compressTime_ += costTime;
489 compressCounts_++;
490 } else {
491 totalTime_ += costTime;
492 }
493 }
494
PrintTransMessage()495 string CompressionParser::PrintTransMessage()
496 {
497 string res = "Processing report:\n";
498 res.append("total:").append(to_string(totalCounts_)).append(", ").append(to_string(totalTime_)).append(" us.\n");
499 res.append("compressed:").append(to_string(compressCounts_)).append(", ").append(to_string(compressTime_))
500 .append(" us.\n");
501 res.append("success:").append(to_string(successCounts_)).append(", ").append(to_string(successTime_))
502 .append(" us, ").append(to_string(originalSize_)).append(" Bytes to ").append(to_string(successSize_))
503 .append(" Bytes.");
504 return res;
505 }
506
GetMediaSwitch()507 bool CompressionParser::GetMediaSwitch()
508 {
509 return mediaSwitch_;
510 }
511
GetDefaultCompress()512 bool CompressionParser::GetDefaultCompress()
513 {
514 return defaultCompress_;
515 }
516
CheckAndTranscode(const string & src,string & dst,string & output,const shared_ptr<CompressFilter> & compressFilter,const bool extAppend)517 bool CompressionParser::CheckAndTranscode(const string &src, string &dst, string &output,
518 const shared_ptr<CompressFilter> &compressFilter, const bool extAppend)
519 {
520 auto t1 = std::chrono::steady_clock::now();
521 TranscodeResult result = {0, 0, 0, 0};
522 if (defaultCompress_) {
523 if (!SetTranscodeOptions(GetMethod(compressFilter), "")) {
524 return false;
525 }
526 auto res = TranscodeImages(src, extAppend, output, result);
527 CollectTimeAndSize(res, t1, result);
528 if (res != TranscodeError::SUCCESS) {
529 return false;
530 }
531 dst = output;
532 return true;
533 }
534 if (!IsInPath(src, compressFilter)) {
535 return false;
536 }
537 if (IsInExcludePath(src, compressFilter)) {
538 if (!SetTranscodeOptions(GetRules(compressFilter), GetExcludeRules(compressFilter))) {
539 return false;
540 }
541 auto res = TranscodeImages(src, extAppend, output, result);
542 CollectTimeAndSize(res, t1, result);
543 if (res != TranscodeError::SUCCESS) {
544 return false;
545 }
546 dst = output;
547 return true;
548 }
549 if (!SetTranscodeOptions(GetRules(compressFilter), "")) {
550 return false;
551 }
552 auto res = TranscodeImages(src, extAppend, output, result);
553 CollectTimeAndSize(res, t1, result);
554 if (res != TranscodeError::SUCCESS) {
555 return false;
556 }
557 dst = output;
558 return true;
559 }
560
CopyForTrans(const string & src,const string & originDst,const string & dst)561 bool CompressionParser::CopyForTrans(const string &src, const string &originDst, const string &dst)
562 {
563 string srcSuffix;
564 string dstSuffix;
565 auto srcIndex = src.find_last_of(".");
566 auto dstIndex = dst.find_last_of(".");
567 if (srcIndex != string::npos && dstIndex != string::npos) {
568 srcSuffix = src.substr(srcIndex + 1);
569 dstSuffix = dst.substr(dstIndex + 1);
570 }
571 auto ret = false;
572 if (srcSuffix == dstSuffix) {
573 ret = ResourceUtil::CopyFileInner(src, dst);
574 } else {
575 uint32_t startIndex = outPath_.size() + CACHES_DIR.size() + 1;
576 string dstPath = outPath_ + SEPARATOR_FILE + RESOURCES_DIR + dst.substr(startIndex);
577 ret = ResourceUtil::CopyFileInner(dst, dstPath);
578 }
579 return ret;
580 }
581
CopyAndTranscode(const string & src,string & dst,const bool extAppend)582 bool CompressionParser::CopyAndTranscode(const string &src, string &dst, const bool extAppend)
583 {
584 auto t0 = std::chrono::steady_clock::now();
585 if (!mediaSwitch_) {
586 auto res = ResourceUtil::CopyFileInner(src, dst);
587 CollectTime(totalCounts_, totalTime_, t0);
588 return res;
589 }
590
591 auto index = dst.find_last_of(SEPARATOR_FILE);
592 if (index == string::npos) {
593 PrintError(GetError(ERR_CODE_INVALID_RESOURCE_PATH).FormatCause(dst.c_str(), "missing separator"));
594 return false;
595 }
596 uint32_t startIndex = outPath_.size() + RESOURCES_DIR.size() + 1;
597 string endStr = dst.substr(startIndex, index - startIndex);
598 string output = outPath_ + SEPARATOR_FILE + CACHES_DIR + endStr;
599 string originDst = dst;
600 if (!ResourceUtil::CreateDirs(output)) {
601 return false;
602 }
603 for (const auto &compressFilter : compressFilters_) {
604 if (!CheckAndTranscode(src, dst, output, compressFilter, extAppend)) {
605 continue;
606 }
607 break;
608 }
609 auto t2 = std::chrono::steady_clock::now();
610 auto ret = CopyForTrans(src, originDst, dst);
611 CollectTime(totalCounts_, totalTime_, t2);
612 return ret;
613 }
614
CheckAndScaleIcon(const std::string & src,const std::string & originDst,std::string & scaleDst)615 bool CompressionParser::CheckAndScaleIcon(const std::string &src, const std::string &originDst, std::string &scaleDst)
616 {
617 scaleDst = src;
618 if (filePath_.empty() || outPath_.empty()) {
619 cout << "Info: compression path or out path is empty, unable to scale icon." << endl;
620 return true;
621 }
622 auto index = originDst.find_last_of(SEPARATOR_FILE);
623 if (index == string::npos) {
624 PrintError(GetError(ERR_CODE_INVALID_RESOURCE_PATH).FormatCause(originDst.c_str(), "missing separator"));
625 return false;
626 }
627 uint32_t startIndex = outPath_.size() + RESOURCES_DIR.size() + 1;
628 string qualifierDir = originDst.substr(startIndex, index - startIndex);
629 string outputCache = outPath_ + SEPARATOR_FILE + CACHES_DIR + qualifierDir;
630 if (!ResourceUtil::CreateDirs(outputCache)) {
631 return false;
632 }
633 string fileName = originDst.substr(index + 1);
634 string outputFile = outputCache + SEPARATOR_FILE + fileName;
635 auto ret = ScaleImage(src, outputFile);
636 if (ret == TranscodeError::SUCCESS) {
637 // if scale success, change src file to scale image
638 scaleDst = outputFile;
639 }
640 return true;
641 }
642
ScaleIconEnable()643 bool CompressionParser::ScaleIconEnable()
644 {
645 return !filePath_.empty() && !outPath_.empty() && handle_ != nullptr;
646 }
647 }
648 }
649 }
650