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