1 /*
2 * Copyright (c) 2021-2025 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 "core/image/image_loader.h"
17 #ifdef ACE_UNITTEST
18 #include <unistd.h>
19 #endif
20 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
21 #ifdef USE_NEW_SKIA
22 #include "src/base/SkBase64.h"
23 #else
24 #include "include/utils/SkBase64.h"
25 #endif
26
27 #include "base/image/file_uri_helper.h"
28 #include "base/image/image_source.h"
29 #include "base/thread/background_task_executor.h"
30 #include "base/utils/resource_configuration.h"
31 #include "base/utils/system_properties.h"
32 #include "core/common/container.h"
33 #include "core/common/resource/resource_configuration.h"
34 #include "core/common/resource/resource_manager.h"
35 #include "core/common/resource/resource_wrapper.h"
36 #include "core/components_ng/image_provider/drawing_image_data.h"
37 #include "core/components_ng/pattern/image/image_dfx.h"
38 #include "core/image/image_file_cache.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40
41 namespace OHOS::Ace {
42 namespace {
43 constexpr size_t FILE_HEAD_LENGTH = 7; // 7 is the size of "file://"
44 constexpr size_t MEMORY_HEAD_LENGTH = 9; // 9 is the size of "memory://"
45 constexpr size_t INTERNAL_FILE_HEAD_LENGTH = 15; // 15 is the size of "internal://app/"
46 // regex for "resource://colormode/xxx.type", colormode can be "light" or "dark", xxx represents system resource id,
47 // type can be "jpg", "png", "svg" and so on.
48 const std::regex MEDIA_RES_ID_REGEX(R"(^resource://\w+/([0-9]+)\.\w+$)", std::regex::icase);
49 const std::regex MEDIA_APP_RES_PATH_REGEX(R"(^resource://RAWFILE/(.*)$)");
50 const std::regex MEDIA_APP_RES_ID_REGEX(R"(^resource://.*/([0-9]+)\.\w+$)", std::regex::icase);
51 const std::regex MEDIA_RES_NAME_REGEX(R"(^resource://.*/(\w+)\.\w+$)", std::regex::icase);
52 constexpr uint32_t MEDIA_RESOURCE_MATCH_SIZE = 2;
53
54 const std::chrono::duration<int, std::milli> TIMEOUT_DURATION(10000);
55
56 #ifdef WINDOWS_PLATFORM
realpath(const char * path,char * resolved_path)57 char* realpath(const char* path, char* resolved_path)
58 {
59 if (strcpy_s(resolved_path, PATH_MAX, path) != 0) {
60 return nullptr;
61 }
62 return resolved_path;
63 }
64 #endif
65
66 #ifndef PREVIEW
67 struct DataWrapper {
68 std::shared_ptr<RSData> data;
69 };
70
DataWrapperReleaseProc(const void *,void * context)71 inline void DataWrapperReleaseProc(const void*, void* context)
72 {
73 DataWrapper* wrapper = reinterpret_cast<DataWrapper*>(context);
74 delete wrapper;
75 }
76 #endif
77 } // namespace
78
RemovePathHead(const std::string & uri)79 std::string ImageLoader::RemovePathHead(const std::string& uri)
80 {
81 auto iter = uri.find_first_of(':');
82 if (iter == std::string::npos) {
83 TAG_LOGW(AceLogTag::ACE_IMAGE, "No scheme, not a File or Memory path");
84 return std::string();
85 }
86 std::string head = uri.substr(0, iter);
87 if ((head == "file" && uri.size() > FILE_HEAD_LENGTH) || (head == "memory" && uri.size() > MEMORY_HEAD_LENGTH) ||
88 (head == "internal" && uri.size() > INTERNAL_FILE_HEAD_LENGTH)) {
89 // the file uri format is like "file:///data/data...",
90 // the memory uri format is like "memory://imagename.png" for example,
91 // iter + 3 to get the absolutely file path substring : "/data/data..." or the image name: "imagename.png"
92 return uri.substr(iter + 3);
93 }
94 TAG_LOGW(AceLogTag::ACE_IMAGE, "Wrong scheme, not a File!");
95 return std::string();
96 }
97
CreateImageLoader(const ImageSourceInfo & imageSourceInfo)98 RefPtr<ImageLoader> ImageLoader::CreateImageLoader(const ImageSourceInfo& imageSourceInfo)
99 {
100 SrcType srcType = imageSourceInfo.GetSrcType();
101 switch (srcType) {
102 case SrcType::INTERNAL:
103 case SrcType::FILE: {
104 return MakeRefPtr<FileImageLoader>();
105 }
106 case SrcType::NETWORK: {
107 return MakeRefPtr<NetworkImageLoader>();
108 }
109 case SrcType::ASSET: {
110 return MakeRefPtr<AssetImageLoader>();
111 }
112 case SrcType::BASE64: {
113 return MakeRefPtr<Base64ImageLoader>();
114 }
115 case SrcType::RESOURCE: {
116 return MakeRefPtr<ResourceImageLoader>();
117 }
118 case SrcType::DATA_ABILITY: {
119 return MakeRefPtr<DataProviderImageLoader>();
120 }
121 case SrcType::DATA_ABILITY_DECODED: {
122 return MakeRefPtr<DecodedDataProviderImageLoader>();
123 }
124 case SrcType::MEMORY: {
125 return MakeRefPtr<SharedMemoryImageLoader>();
126 }
127 case SrcType::RESOURCE_ID: {
128 return MakeRefPtr<InternalImageLoader>();
129 }
130 case SrcType::PIXMAP: {
131 return MakeRefPtr<PixelMapImageLoader>();
132 }
133 case SrcType::ASTC: {
134 return MakeRefPtr<AstcImageLoader>();
135 }
136 default: {
137 return nullptr;
138 }
139 }
140 }
141
LoadDataFromCachedFile(const std::string & uri)142 std::shared_ptr<RSData> ImageLoader::LoadDataFromCachedFile(const std::string& uri)
143 {
144 std::string cacheFilePath = ImageFileCache::GetInstance().GetImageCacheFilePath(uri);
145 if (cacheFilePath.length() > PATH_MAX) {
146 TAG_LOGW(
147 AceLogTag::ACE_IMAGE, "cache file path is too long, cacheFilePath: %{private}s", cacheFilePath.c_str());
148 return nullptr;
149 }
150 cacheFilePath = ImageFileCache::GetInstance().GetCacheFilePath(uri);
151 if (cacheFilePath == "") {
152 return nullptr;
153 }
154 char realPath[PATH_MAX] = { 0x00 };
155 if (realpath(cacheFilePath.c_str(), realPath) == nullptr) {
156 TAG_LOGW(AceLogTag::ACE_IMAGE, "realpath fail! cacheFilePath: %{private}s, fail reason: %{public}s",
157 cacheFilePath.c_str(), strerror(errno));
158 return nullptr;
159 }
160 return RSData::MakeFromFileName(realPath);
161 }
162
QueryImageDataFromImageCache(const ImageSourceInfo & sourceInfo)163 std::shared_ptr<RSData> ImageLoader::QueryImageDataFromImageCache(const ImageSourceInfo& sourceInfo)
164 {
165 ACE_LAYOUT_SCOPED_TRACE("QueryImageDataFromImageCache[%s]", sourceInfo.ToString().c_str());
166 auto pipelineCtx = PipelineContext::GetCurrentContext();
167 CHECK_NULL_RETURN(pipelineCtx, nullptr);
168 auto imageCache = pipelineCtx->GetImageCache();
169 CHECK_NULL_RETURN(imageCache, nullptr);
170 auto cacheData = imageCache->GetCacheImageData(sourceInfo.GetKey());
171 CHECK_NULL_RETURN(cacheData, nullptr);
172 auto rosenCachedImageData = AceType::DynamicCast<NG::DrawingImageData>(cacheData);
173 CHECK_NULL_RETURN(rosenCachedImageData, nullptr);
174 return rosenCachedImageData->GetRSData();
175 }
176
CacheImageData(const std::string & key,const RefPtr<NG::ImageData> & imageData)177 void ImageLoader::CacheImageData(const std::string& key, const RefPtr<NG::ImageData>& imageData)
178 {
179 auto pipelineCtx = PipelineContext::GetCurrentContext();
180 CHECK_NULL_VOID(pipelineCtx);
181 auto imageCache = pipelineCtx->GetImageCache();
182 CHECK_NULL_VOID(imageCache);
183 imageCache->CacheImageData(key, imageData);
184 }
185
LoadImageDataFromFileCache(const std::string & key,const std::string & suffix)186 RefPtr<NG::ImageData> ImageLoader::LoadImageDataFromFileCache(const std::string& key, const std::string& suffix)
187 {
188 ACE_FUNCTION_TRACE();
189 return ImageFileCache::GetInstance().GetDataFromCacheFile(key, suffix);
190 }
191
192 // NG ImageLoader entrance
GetImageData(const ImageSourceInfo & src,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)193 RefPtr<NG::ImageData> ImageLoader::GetImageData(
194 const ImageSourceInfo& src, NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
195 {
196 ACE_SCOPED_TRACE("GetImageData %s", src.ToString().c_str());
197 if (src.IsPixmap()) {
198 return LoadDecodedImageData(src, loadResultInfo, context);
199 }
200 std::shared_ptr<RSData> rsData = nullptr;
201 do {
202 rsData = ImageLoader::QueryImageDataFromImageCache(src);
203 if (rsData) {
204 break;
205 }
206 rsData = LoadImageData(src, loadResultInfo, context);
207 CHECK_NULL_RETURN(rsData, nullptr);
208 ImageLoader::CacheImageData(src.GetKey(), AceType::MakeRefPtr<NG::DrawingImageData>(rsData));
209 } while (false);
210 return AceType::MakeRefPtr<NG::DrawingImageData>(rsData);
211 }
212
213 // NG ImageLoader entrance
DownloadImage(DownloadCallback && downloadCallback,const std::string & src,bool sync)214 bool NetworkImageLoader::DownloadImage(DownloadCallback&& downloadCallback, const std::string& src, bool sync)
215 {
216 return sync ? DownloadManager::GetInstance()->DownloadSyncWithPreload(
217 std::move(downloadCallback), src, Container::CurrentId())
218 : DownloadManager::GetInstance()->DownloadAsyncWithPreload(
219 std::move(downloadCallback), src, Container::CurrentId());
220 }
221
BuildImageData(const std::shared_ptr<RSData> & result)222 std::shared_ptr<RSData> FileImageLoader::BuildImageData(const std::shared_ptr<RSData>& result)
223 {
224 CHECK_NULL_RETURN(result, nullptr);
225 auto rsData = std::make_shared<RSData>();
226 CHECK_NULL_RETURN(rsData, nullptr);
227 #ifdef PREVIEW
228 return rsData->BuildWithCopy(result->GetData(), result->GetSize()) ? rsData : nullptr;
229 #else
230 DataWrapper* wrapper = new DataWrapper{ std::move(result) };
231 CHECK_NULL_RETURN(wrapper, nullptr);
232 if (wrapper->data == nullptr) {
233 delete wrapper;
234 return nullptr;
235 }
236 if (!rsData->BuildWithProc(wrapper->data->GetData(), wrapper->data->GetSize(), DataWrapperReleaseProc, wrapper)) {
237 if (wrapper) {
238 delete wrapper;
239 }
240 return nullptr;
241 }
242 return rsData;
243 #endif
244 }
245
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> &)246 std::shared_ptr<RSData> FileImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
247 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& /* context */)
248 {
249 auto& errorInfo = loadResultInfo.errorInfo;
250 const auto& src = imageSourceInfo.GetSrc();
251 std::string filePath = RemovePathHead(src);
252 auto imageDfxConfig = imageSourceInfo.GetImageDfxConfig();
253 ACE_SCOPED_TRACE("LoadImageData %s", imageDfxConfig.ToStringWithSrc().c_str());
254 if (imageSourceInfo.GetSrcType() == SrcType::INTERNAL) {
255 // the internal source uri format is like "internal://app/imagename.png", the absolute path of which is like
256 // "/data/data/{bundleName}/files/imagename.png"
257 auto bundleName = Container::CurrentBundleName();
258 if (bundleName.empty()) {
259 TAG_LOGW(AceLogTag::ACE_IMAGE,
260 "bundleName is empty, LoadImageData for internal source fail! %{private}s-%{public}s.",
261 imageDfxConfig.GetImageSrc().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
262 return nullptr;
263 }
264 if (!StringUtils::StartWith(filePath, "app/")) { // "app/" is infix of internal path
265 TAG_LOGW(AceLogTag::ACE_IMAGE, "internal path format is wrong. path is %{private}s-%{public}s.",
266 src.c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
267 return nullptr;
268 }
269 filePath = std::string("/data/data/") // head of absolute path
270 .append(bundleName)
271 .append("/files/") // infix of absolute path
272 .append(filePath.substr(4)); // 4 is the length of "app/" from "internal://app/"
273 } else if (imageSourceInfo.GetSrcType() == SrcType::FILE) {
274 filePath = FileUriHelper::GetRealPath(src);
275 }
276 if (filePath.length() > PATH_MAX) {
277 TAG_LOGW(AceLogTag::ACE_IMAGE, "path is too long. %{public}s.", imageDfxConfig.ToStringWithoutSrc().c_str());
278 errorInfo = { ImageErrorCode::GET_IMAGE_FILE_PATH_TOO_LONG, "path is too long." };
279 return nullptr;
280 }
281 char realPath[PATH_MAX] = { 0x00 };
282 realpath(filePath.c_str(), realPath);
283 auto result = RSData::MakeFromFileName(realPath);
284 if (!result) {
285 TAG_LOGW(AceLogTag::ACE_IMAGE,
286 "read data failed, filePath: %{private}s, realPath: %{private}s, "
287 "src: %{private}s, fail reason: %{private}s.%{public}s.",
288 filePath.c_str(), src.c_str(), realPath, strerror(errno), imageDfxConfig.ToStringWithoutSrc().c_str());
289 errorInfo = { ImageErrorCode::GET_IMAGE_FILE_READ_DATA_FAILED, "read data failed." };
290 return nullptr;
291 } else {
292 loadResultInfo.fileSize = result->GetSize();
293 ACE_SCOPED_TRACE("LoadImageData result %s - %d", imageDfxConfig.ToStringWithSrc().c_str(),
294 static_cast<int32_t>(result->GetSize()));
295 TAG_LOGI(AceLogTag::ACE_IMAGE, "Read data %{private}s - %{public}s : %{public}d", realPath,
296 imageDfxConfig.ToStringWithoutSrc().c_str(), static_cast<int32_t>(result->GetSize()));
297 }
298 return BuildImageData(result);
299 }
300
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)301 std::shared_ptr<RSData> DataProviderImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
302 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
303 {
304 auto& errorInfo = loadResultInfo.errorInfo;
305 const auto& src = imageSourceInfo.GetSrc();
306 auto drawingData = ImageLoader::LoadDataFromCachedFile(src);
307 if (drawingData) {
308 return drawingData;
309 }
310 auto pipeline = context.Upgrade();
311 CHECK_NULL_RETURN(pipeline, nullptr);
312 auto dataProvider = pipeline->GetDataProviderManager();
313 CHECK_NULL_RETURN(dataProvider, nullptr);
314 auto res = dataProvider->GetDataProviderResFromUri(src, errorInfo);
315 CHECK_NULL_RETURN(res, nullptr);
316 auto data = std::make_shared<RSData>();
317 data->BuildFromMalloc(res->GetData().release(), res->GetSize());
318 return data;
319 }
320
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)321 std::shared_ptr<RSData> AssetImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
322 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
323 {
324 auto& errorInfo = loadResultInfo.errorInfo;
325 ACE_FUNCTION_TRACE();
326 const auto& src = imageSourceInfo.GetSrc();
327 auto imageDfxConfig = imageSourceInfo.GetImageDfxConfig();
328 if (src.empty()) {
329 TAG_LOGW(AceLogTag::ACE_IMAGE, "image src is empty. %{public}s.", imageDfxConfig.ToStringWithoutSrc().c_str());
330 errorInfo = { ImageErrorCode::GET_IMAGE_ASSET_URI_INVALID, "uri is invalid." };
331 return nullptr;
332 }
333
334 std::string assetSrc(src);
335 if (assetSrc[0] == '/') {
336 assetSrc = assetSrc.substr(1); // get the asset src without '/'.
337 } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
338 assetSrc = assetSrc.substr(2); // get the asset src without './'.
339 }
340 auto pipelineContext = context.Upgrade();
341 if (!pipelineContext) {
342 TAG_LOGW(
343 AceLogTag::ACE_IMAGE, "invalid pipeline context. %{public}s", imageDfxConfig.ToStringWithoutSrc().c_str());
344 return nullptr;
345 }
346 auto assetManager = pipelineContext->GetAssetManager();
347 if (!assetManager) {
348 TAG_LOGW(AceLogTag::ACE_IMAGE, "NoAssetManager! %{public}s", imageDfxConfig.ToStringWithoutSrc().c_str());
349 return nullptr;
350 }
351 auto assetData = assetManager->GetAsset(assetSrc);
352 if (!assetData) {
353 TAG_LOGW(AceLogTag::ACE_IMAGE, "NoAssetData-%{public}s", imageDfxConfig.ToStringWithoutSrc().c_str());
354 errorInfo = { ImageErrorCode::GET_IMAGE_ASSET_GET_FAILED, "get asset failed." };
355 return nullptr;
356 }
357 const uint8_t* data = assetData->GetData();
358 const size_t dataSize = assetData->GetSize();
359 auto drawingData = std::make_shared<RSData>();
360 drawingData->BuildWithCopy(data, dataSize);
361 return drawingData;
362 }
363
LoadJsonData(const std::string & src,const WeakPtr<PipelineBase> context)364 std::string AssetImageLoader::LoadJsonData(const std::string& src, const WeakPtr<PipelineBase> context)
365 {
366 if (src.empty()) {
367 TAG_LOGW(AceLogTag::ACE_IMAGE, "image src is empty");
368 return "";
369 }
370
371 std::string assetSrc(src);
372 if (assetSrc[0] == '/') {
373 assetSrc = assetSrc.substr(1); // get the asset src without '/'.
374 } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
375 assetSrc = assetSrc.substr(2); // get the asset src without './'.
376 }
377 auto pipelineContext = context.Upgrade();
378 if (!pipelineContext) {
379 return "";
380 }
381 auto assetManager = pipelineContext->GetAssetManager();
382 if (!assetManager) {
383 TAG_LOGW(AceLogTag::ACE_IMAGE, "No asset manager!");
384 return "";
385 }
386 auto assetData = assetManager->GetAsset(assetSrc);
387 if (!assetData || !assetData->GetData()) {
388 TAG_LOGW(AceLogTag::ACE_IMAGE, "No asset data!");
389 return "";
390 }
391 return std::string((char*)assetData->GetData(), assetData->GetSize());
392 }
393
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)394 std::shared_ptr<RSData> NetworkImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
395 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
396 {
397 auto uri = imageSourceInfo.GetSrc();
398 auto pipelineContext = context.Upgrade();
399 auto imageDfxConfig = imageSourceInfo.GetImageDfxConfig();
400 if (!pipelineContext || pipelineContext->IsJsCard()) {
401 TAG_LOGW(AceLogTag::ACE_IMAGE, "network image in JS card is forbidden. %{public}s",
402 imageDfxConfig.ToStringWithoutSrc().c_str());
403 return nullptr;
404 }
405 // 1. find in cache file path.
406 auto drawingData = ImageLoader::LoadDataFromCachedFile(uri);
407 if (drawingData) {
408 return drawingData;
409 }
410
411 // 2. if not found. download it.
412 if (SystemProperties::GetDebugEnabled()) {
413 TAG_LOGD(AceLogTag::ACE_IMAGE, "Download network image by loader, uri=%{private}s, %{public}s", uri.c_str(),
414 imageDfxConfig.ToStringWithoutSrc().c_str());
415 }
416
417 #ifndef OHOS_PLATFORM
418 std::vector<uint8_t> imageData;
419 if (!DownloadManager::GetInstance()->Download(uri, imageData) || imageData.empty()) {
420 return nullptr;
421 }
422 auto data = std::make_shared<RSData>();
423 data->BuildWithCopy(imageData.data(), imageData.size());
424 WriteCacheToFile(uri, imageData);
425 #else
426 auto downloadResult = std::make_shared<DownloadResult>();
427 if (!DownloadManager::GetInstance()->Download(uri, downloadResult)) {
428 return nullptr;
429 }
430 auto data = std::make_shared<RSData>();
431 data->BuildWithCopy(downloadResult->dataOut.data(), downloadResult->dataOut.size());
432 WriteCacheToFile(uri, downloadResult->dataOut);
433 #endif
434 return data;
435 }
436
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)437 std::shared_ptr<RSData> InternalImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
438 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
439 {
440 size_t imageSize = 0;
441 const uint8_t* internalData =
442 InternalResource::GetInstance().GetResource(imageSourceInfo.GetResourceId(), imageSize);
443 if (internalData == nullptr) {
444 auto imageDfxConfig = imageSourceInfo.GetImageDfxConfig();
445 TAG_LOGW(AceLogTag::ACE_IMAGE, "Resource is invalid, %{public}s", imageDfxConfig.ToStringWithoutSrc().c_str());
446 return nullptr;
447 }
448 auto drawingData = std::make_shared<RSData>();
449 drawingData->BuildWithCopy(internalData, imageSize);
450 return drawingData;
451 }
452
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)453 std::shared_ptr<RSData> Base64ImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
454 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
455 {
456 auto& errorInfo = loadResultInfo.errorInfo;
457 std::string_view base64Code = GetBase64ImageCode(imageSourceInfo.GetSrc());
458 auto imageDfxConfig = imageSourceInfo.GetImageDfxConfig();
459 if (base64Code.size() == 0) {
460 TAG_LOGW(AceLogTag::ACE_IMAGE, "base64Code = %{private}s is empty. %{public}s.",
461 imageDfxConfig.GetImageSrc().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
462 errorInfo = { ImageErrorCode::GET_IMAGE_BASE_WRONG_CODE_FORMAT, "wrong code format." };
463 return nullptr;
464 }
465
466 size_t outputLen;
467 SkBase64::Error error = SkBase64::Decode(base64Code.data(), base64Code.size(), nullptr, &outputLen);
468 if (error != SkBase64::Error::kNoError) {
469 TAG_LOGW(AceLogTag::ACE_IMAGE,
470 "error base64 image code = %{public}d! Base64Size = %{public}d, outputLen = %{public}d, %{public}s",
471 static_cast<int32_t>(base64Code.size()), static_cast<int32_t>(error), static_cast<int32_t>(outputLen),
472 imageDfxConfig.ToStringWithoutSrc().c_str());
473 errorInfo = { ImageErrorCode::GET_IMAGE_BASE_DECODE_IMAGE_FAILED, "decode base64 image failed." };
474 return nullptr;
475 }
476
477 auto resData = std::make_shared<RSData>();
478 resData->BuildUninitialized(outputLen);
479 void* output = resData->WritableData();
480
481 error = SkBase64::Decode(base64Code.data(), base64Code.size(), output, &outputLen);
482 if (error != SkBase64::Error::kNoError) {
483 TAG_LOGW(AceLogTag::ACE_IMAGE,
484 "error base64 image code = %{public}d! Base64Size = %{public}d, outputLen = %{public}d, %{public}s",
485 static_cast<int32_t>(base64Code.size()), static_cast<int32_t>(error), static_cast<int32_t>(outputLen),
486 imageDfxConfig.ToStringWithoutSrc().c_str());
487 errorInfo = { ImageErrorCode::GET_IMAGE_BASE_DECODE_IMAGE_FAILED, "decode base64 image failed." };
488 return nullptr;
489 }
490 if (SystemProperties::GetDebugEnabled()) {
491 TAG_LOGD(AceLogTag::ACE_IMAGE, "base64 size=%{public}d, src=%{private}s. %{public}s.", (int)base64Code.size(),
492 imageSourceInfo.ToString().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
493 }
494 return resData;
495 }
496
GetBase64ImageCode(const std::string & uri)497 std::string_view Base64ImageLoader::GetBase64ImageCode(const std::string& uri)
498 {
499 auto iter = uri.find_first_of(',');
500 if (iter == std::string::npos || ((uri.size() > 0) && (iter == uri.size() - 1))) {
501 TAG_LOGW(AceLogTag::ACE_IMAGE, "wrong code format!");
502 return std::string_view();
503 }
504 // iter + 1 to skip the ","
505 std::string_view code(uri.c_str() + (iter + 1));
506 return code;
507 }
508
GetResourceId(const std::string & uri,uint32_t & resId) const509 bool ResourceImageLoader::GetResourceId(const std::string& uri, uint32_t& resId) const
510 {
511 std::smatch matches;
512 if (std::regex_match(uri, matches, MEDIA_RES_ID_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
513 resId = static_cast<uint32_t>(std::stoul(matches[1].str()));
514 return true;
515 }
516
517 std::smatch appMatches;
518 if (std::regex_match(uri, appMatches, MEDIA_APP_RES_ID_REGEX) && appMatches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
519 resId = static_cast<uint32_t>(std::stoul(appMatches[1].str()));
520 return true;
521 }
522
523 return false;
524 }
525
GetResourceId(const std::string & uri,std::string & path) const526 bool ResourceImageLoader::GetResourceId(const std::string& uri, std::string& path) const
527 {
528 std::smatch matches;
529 if (std::regex_match(uri, matches, MEDIA_APP_RES_PATH_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
530 path = matches[1].str();
531 return true;
532 }
533
534 return false;
535 }
536
GetResourceName(const std::string & uri,std::string & resName) const537 bool ResourceImageLoader::GetResourceName(const std::string& uri, std::string& resName) const
538 {
539 std::smatch matches;
540 if (std::regex_match(uri, matches, MEDIA_RES_NAME_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
541 resName = matches[1].str();
542 return true;
543 }
544
545 return false;
546 }
547
LoadImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)548 std::shared_ptr<RSData> ResourceImageLoader::LoadImageData(const ImageSourceInfo& imageSourceInfo,
549 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
550 {
551 auto& errorInfo = loadResultInfo.errorInfo;
552 int32_t instanceId = Container::CurrentIdSafely();
553 auto uri = imageSourceInfo.GetSrc();
554 auto bundleName = imageSourceInfo.GetBundleName();
555 auto moudleName = imageSourceInfo.GetModuleName();
556
557 auto resourceObject = AceType::MakeRefPtr<ResourceObject>(bundleName, moudleName, instanceId);
558 RefPtr<ResourceAdapter> resourceAdapter = nullptr;
559 RefPtr<ThemeConstants> themeConstants = nullptr;
560 if (SystemProperties::GetResourceDecoupling()) {
561 auto adapterInCache = ResourceManager::GetInstance().GetOrCreateResourceAdapter(resourceObject);
562 CHECK_NULL_RETURN(adapterInCache, nullptr);
563 ResourceConfiguration resConfig;
564 if (imageSourceInfo.GetLocalColorMode() != ColorMode::COLOR_MODE_UNDEFINED) {
565 resConfig.SetColorMode(imageSourceInfo.GetLocalColorMode());
566 } else {
567 resConfig.SetColorMode(Container::CurrentColorMode());
568 }
569 ConfigurationChange configChange { .colorModeUpdate = true };
570 resourceAdapter = adapterInCache->GetOverrideResourceAdapter(resConfig, configChange);
571 } else {
572 auto themeManager = PipelineBase::CurrentThemeManager();
573 CHECK_NULL_RETURN(themeManager, nullptr);
574 themeConstants = themeManager->GetThemeConstants();
575 CHECK_NULL_RETURN(themeConstants, nullptr);
576 }
577 auto resourceWrapper = AceType::MakeRefPtr<ResourceWrapper>(themeConstants, resourceAdapter);
578
579 std::unique_ptr<uint8_t[]> data;
580 size_t dataLen = 0;
581 std::string rawFile;
582 if (GetResourceId(uri, rawFile)) {
583 // must fit raw file firstly, as file name may contains number
584 if (!resourceWrapper->GetRawFileData(rawFile, dataLen, data, bundleName, moudleName)) {
585 TAG_LOGW(AceLogTag::ACE_IMAGE, "get image data by name failed, uri:%{private}s, rawFile:%{private}s",
586 uri.c_str(), rawFile.c_str());
587 errorInfo = { ImageErrorCode::GET_IMAGE_RESOURCE_GET_DATA_BY_NAME_FAILED,
588 "get image data by name failed." };
589 return nullptr;
590 }
591 auto drawingData = std::make_shared<RSData>();
592 drawingData->BuildWithCopy(data.get(), dataLen);
593 return drawingData;
594 }
595 uint32_t resId = 0;
596 if (!imageSourceInfo.GetIsUriPureNumber() && GetResourceId(uri, resId)) {
597 if (resourceWrapper->GetMediaData(resId, dataLen, data, bundleName, moudleName)) {
598 auto drawingData = std::make_shared<RSData>();
599 drawingData->BuildWithCopy(data.get(), dataLen);
600 return drawingData;
601 } else {
602 TAG_LOGW(AceLogTag::ACE_IMAGE, "get image data by id failed, uri:%{private}s, id:%{public}u", uri.c_str(),
603 resId);
604 errorInfo = { ImageErrorCode::GET_IMAGE_RESOURCE_GET_DATA_BY_ID_FAILED, "get image data by id failed." };
605 }
606 }
607 std::string resName;
608 if (GetResourceName(uri, resName)) {
609 if (!resourceWrapper->GetMediaData(resName, dataLen, data, bundleName, moudleName)) {
610 TAG_LOGW(AceLogTag::ACE_IMAGE, "get image data by name failed, uri:%{private}s, resName:%{private}s",
611 uri.c_str(), resName.c_str());
612 errorInfo = { ImageErrorCode::GET_IMAGE_RESOURCE_GET_DATA_BY_NAME_FAILED,
613 "get image data by name failed." };
614 return nullptr;
615 }
616 auto drawingData = std::make_shared<RSData>();
617 drawingData->BuildWithCopy(data.get(), dataLen);
618 errorInfo = { ImageErrorCode::DEFAULT, "" };
619 return drawingData;
620 }
621 TAG_LOGW(AceLogTag::ACE_IMAGE, "load image data failed, as uri is invalid:%{private}s", uri.c_str());
622 if (errorInfo.errorCode == ImageErrorCode::DEFAULT) {
623 errorInfo = { ImageErrorCode::GET_IMAGE_RESOURCE_URI_INVALID, "uri is invalid." };
624 }
625 return nullptr;
626 }
627
LoadImageData(const ImageSourceInfo &,NG::ImageLoadResultInfo &,const WeakPtr<PipelineBase> &)628 std::shared_ptr<RSData> DecodedDataProviderImageLoader::LoadImageData(const ImageSourceInfo& /* imageSourceInfo */,
629 NG::ImageLoadResultInfo& /* errorInfo */, const WeakPtr<PipelineBase>& /* context */)
630 {
631 return nullptr;
632 }
633
634 // return orientation of pixmap for cache key
GetThumbnailOrientation(const ImageSourceInfo & src)635 std::string DecodedDataProviderImageLoader::GetThumbnailOrientation(const ImageSourceInfo& src)
636 {
637 auto pipeline = PipelineContext::GetCurrentContext();
638 CHECK_NULL_RETURN(pipeline, "");
639 auto dataProvider = pipeline->GetDataProviderManager();
640 CHECK_NULL_RETURN(dataProvider, "");
641
642 // get file fd
643 // concat to get file path ("datashare://media/xx")
644 auto path = src.GetSrc();
645 auto pos = path.find("/thumbnail");
646 path = path.substr(0, pos);
647 int32_t fd = dataProvider->GetDataProviderFile(path, "r");
648 CHECK_NULL_RETURN(fd >= 0, "");
649
650 // check image orientation
651 auto imageSrc = ImageSource::Create(fd);
652 close(fd);
653 CHECK_NULL_RETURN(imageSrc, "");
654 std::string orientation = imageSrc->GetProperty("Orientation");
655 return orientation;
656 }
657
LoadDecodedImageData(const ImageSourceInfo & src,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & pipelineWk)658 RefPtr<NG::ImageData> DecodedDataProviderImageLoader::LoadDecodedImageData(
659 const ImageSourceInfo& src, NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& pipelineWk)
660 {
661 #ifndef PIXEL_MAP_SUPPORTED
662 return nullptr;
663 #else
664 auto& errorInfo = loadResultInfo.errorInfo;
665 ACE_FUNCTION_TRACE();
666 auto pipeline = pipelineWk.Upgrade();
667 auto imageDfxConfig = src.GetImageDfxConfig();
668 if (!pipeline) {
669 TAG_LOGW(AceLogTag::ACE_IMAGE, "Pipeline is empty in decodeData. %{public}s.",
670 imageDfxConfig.ToStringWithoutSrc().c_str());
671 return nullptr;
672 }
673 auto dataProvider = pipeline->GetDataProviderManager();
674 if (!dataProvider) {
675 TAG_LOGW(AceLogTag::ACE_IMAGE, "dataProvider is empty in pipeline. %{public}s.",
676 imageDfxConfig.ToStringWithoutSrc().c_str());
677 return nullptr;
678 }
679
680 void* pixmapMediaUniquePtr = dataProvider->GetDataProviderThumbnailResFromUri(src.GetSrc());
681 auto pixmap = PixelMap::CreatePixelMapFromDataAbility(pixmapMediaUniquePtr);
682 if (!pixmap) {
683 TAG_LOGW(AceLogTag::ACE_IMAGE, "DecodeData is Empty. %{public}s.", imageDfxConfig.ToStringWithoutSrc().c_str());
684 errorInfo = { ImageErrorCode::GET_IMAGE_DECODE_DATA_PROVIDER_DATA_EMPTY, "decoded data is empty." };
685 return nullptr;
686 }
687 TAG_LOGI(AceLogTag::ACE_IMAGE,
688 "src=%{private}s, pixmap from Media width*height=%{public}d*%{public}d, ByteCount=%{public}d. %{public}s",
689 src.ToString().c_str(), pixmap->GetWidth(), pixmap->GetHeight(), pixmap->GetByteCount(),
690 imageDfxConfig.ToStringWithoutSrc().c_str());
691 if (SystemProperties::GetDebugPixelMapSaveEnabled()) {
692 pixmap->SavePixelMapToFile("_fromMedia_");
693 }
694
695 auto cache = pipeline->GetImageCache();
696 if (SystemProperties::GetDebugEnabled()) {
697 TAG_LOGD(AceLogTag::ACE_IMAGE, "DecodedDataProvider src=%{private}s,Key=%{private}s. %{public}s.",
698 src.ToString().c_str(), src.GetKey().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
699 }
700 if (cache) {
701 cache->CacheImageData(src.GetKey(), MakeRefPtr<NG::PixmapData>(pixmap));
702 }
703 return MakeRefPtr<NG::PixmapData>(pixmap);
704 #endif
705 }
706
LoadImageData(const ImageSourceInfo &,NG::ImageLoadResultInfo &,const WeakPtr<PipelineBase> &)707 std::shared_ptr<RSData> PixelMapImageLoader::LoadImageData(const ImageSourceInfo& /* imageSourceInfo */,
708 NG::ImageLoadResultInfo& /* loadResultInfo */, const WeakPtr<PipelineBase>& /* context */)
709 {
710 return nullptr;
711 }
712
LoadDecodedImageData(const ImageSourceInfo & imageSourceInfo,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & context)713 RefPtr<NG::ImageData> PixelMapImageLoader::LoadDecodedImageData(const ImageSourceInfo& imageSourceInfo,
714 NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& context)
715 {
716 #ifndef PIXEL_MAP_SUPPORTED
717 return nullptr;
718 #else
719 auto imageDfxConfig = imageSourceInfo.GetImageDfxConfig();
720 if (!imageSourceInfo.GetPixmap()) {
721 TAG_LOGW(AceLogTag::ACE_IMAGE, "no pixel map in imageSourceInfo, imageSourceInfo: %{private}s. %{public}s.",
722 imageSourceInfo.ToString().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
723 return nullptr;
724 }
725 if (SystemProperties::GetDebugEnabled()) {
726 TAG_LOGD(AceLogTag::ACE_IMAGE, "src is pixmap %{private}s, %{public}s.", imageSourceInfo.ToString().c_str(),
727 imageDfxConfig.ToStringWithoutSrc().c_str());
728 }
729 return MakeRefPtr<NG::PixmapData>(imageSourceInfo.GetPixmap());
730 #endif
731 }
732
LoadImageData(const ImageSourceInfo & src,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & pipelineWk)733 std::shared_ptr<RSData> SharedMemoryImageLoader::LoadImageData(
734 const ImageSourceInfo& src, NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& pipelineWk)
735 {
736 auto& errorInfo = loadResultInfo.errorInfo;
737 CHECK_RUN_ON(BACKGROUND);
738 auto imageDfxConfig = src.GetImageDfxConfig();
739 auto pipeline = pipelineWk.Upgrade();
740 if (!pipeline) {
741 TAG_LOGW(AceLogTag::ACE_IMAGE, "Pipeline is empty in sharedImageLoader. %{public}s.",
742 imageDfxConfig.ToStringWithoutSrc().c_str());
743 return nullptr;
744 }
745 auto manager = pipeline->GetOrCreateSharedImageManager();
746 auto id = RemovePathHead(src.GetSrc());
747 bool found = manager->FindImageInSharedImageMap(id, AceType::WeakClaim(this));
748 // image data not ready yet, wait for data
749 if (!found) {
750 manager->RegisterLoader(id, AceType::WeakClaim(this));
751 // wait for SharedImageManager to notify
752 std::unique_lock<std::mutex> lock(mtx_);
753 auto status = cv_.wait_for(lock, TIMEOUT_DURATION);
754 if (status == std::cv_status::timeout) {
755 TAG_LOGW(AceLogTag::ACE_IMAGE, "load SharedMemoryImage timeout! %{private}s, %{public}s.",
756 imageDfxConfig.GetImageSrc().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
757 errorInfo = { ImageErrorCode::GET_IMAGE_SHARED_MEMORY_LOAD_TIMEOUT,
758 "load shared memory image data timeout." };
759 return nullptr;
760 }
761 }
762
763 std::unique_lock<std::mutex> lock(mtx_);
764 auto drawingData = std::make_shared<RSData>();
765 drawingData->BuildWithCopy(data_.data(), data_.size());
766 return drawingData;
767 }
768
UpdateData(const std::string & uri,const std::vector<uint8_t> & memData)769 void SharedMemoryImageLoader::UpdateData(const std::string& uri, const std::vector<uint8_t>& memData)
770 {
771 TAG_LOGI(AceLogTag::ACE_IMAGE, "SharedMemory image data is ready %{private}s", uri.c_str());
772 {
773 std::scoped_lock<std::mutex> lock(mtx_);
774 data_ = memData;
775 }
776
777 cv_.notify_one();
778 }
779
LoadImageData(const ImageSourceInfo &,NG::ImageLoadResultInfo &,const WeakPtr<PipelineBase> &)780 std::shared_ptr<RSData> AstcImageLoader::LoadImageData(const ImageSourceInfo& /* ImageSourceInfo */,
781 NG::ImageLoadResultInfo& /* errorInfo */, const WeakPtr<PipelineBase>& /* context */)
782 {
783 return nullptr;
784 }
785
LoadDecodedImageData(const ImageSourceInfo & src,NG::ImageLoadResultInfo & loadResultInfo,const WeakPtr<PipelineBase> & pipelineWK)786 RefPtr<NG::ImageData> AstcImageLoader::LoadDecodedImageData(
787 const ImageSourceInfo& src, NG::ImageLoadResultInfo& loadResultInfo, const WeakPtr<PipelineBase>& pipelineWK)
788 {
789 #ifndef PIXEL_MAP_SUPPORTED
790 return nullptr;
791 #else
792 ACE_FUNCTION_TRACE();
793 auto imageDfxConfig = src.GetImageDfxConfig();
794 auto pipeline = pipelineWK.Upgrade();
795 if (!pipeline) {
796 TAG_LOGW(AceLogTag::ACE_IMAGE, "Pipeline is empty in sharedImageLoader. Src = %{private}s-%{public}s.",
797 imageDfxConfig.GetImageSrc().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
798 return nullptr;
799 }
800 auto dataProvider = pipeline->GetDataProviderManager();
801 if (!dataProvider) {
802 TAG_LOGW(AceLogTag::ACE_IMAGE, "DataProvider is empty in AstcLoading. Src = %{private}s, %{public}s.",
803 imageDfxConfig.GetImageSrc().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
804 return nullptr;
805 }
806
807 void* pixmapMediaUniquePtr = dataProvider->GetDataProviderThumbnailResFromUri(src.GetSrc());
808 auto pixmap = PixelMap::CreatePixelMapFromDataAbility(pixmapMediaUniquePtr);
809 if (!pixmap) {
810 TAG_LOGW(AceLogTag::ACE_IMAGE, "Pixelmap is Empty. Src = %{private}s, %{public}s.",
811 imageDfxConfig.GetImageSrc().c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
812 return nullptr;
813 }
814 if (SystemProperties::GetDebugEnabled()) {
815 TAG_LOGD(AceLogTag::ACE_IMAGE,
816 "src=%{public}s, astc pixmap from Media width*height=%{public}d*%{public}d, ByteCount=%{public}d.",
817 imageDfxConfig.ToStringWithSrc().c_str(), pixmap->GetWidth(), pixmap->GetHeight(), pixmap->GetByteCount());
818 }
819
820 if (SystemProperties::GetDebugPixelMapSaveEnabled()) {
821 pixmap->SavePixelMapToFile(imageDfxConfig.ToStringWithoutSrc() + "_ASTC_LOADER_");
822 }
823
824 auto cache = pipeline->GetImageCache();
825 if (cache) {
826 cache->CacheImageData(src.GetKey(), MakeRefPtr<NG::PixmapData>(pixmap));
827 }
828 return MakeRefPtr<NG::PixmapData>(pixmap);
829 #endif
830 }
831
GetThumbnailOrientation(const ImageSourceInfo & src)832 std::string AstcImageLoader::GetThumbnailOrientation(const ImageSourceInfo& src)
833 {
834 auto pipeline = PipelineContext::GetCurrentContext();
835 CHECK_NULL_RETURN(pipeline, "");
836 auto dataProvider = pipeline->GetDataProviderManager();
837 CHECK_NULL_RETURN(dataProvider, "");
838
839 auto path = src.GetSrc();
840 auto pos = path.find("/astc");
841 path = path.substr(0, pos);
842 int32_t fd = dataProvider->GetDataProviderFile(path, "r");
843 CHECK_NULL_RETURN(fd >= 0, "");
844
845 auto imageSrc = ImageSource::Create(fd);
846 close(fd);
847 CHECK_NULL_RETURN(imageSrc, "");
848 std::string orientation = imageSrc->GetProperty("Orientation");
849 return orientation;
850 }
851
WriteCacheToFile(const std::string & uri,const std::vector<uint8_t> & imageData)852 void ImageLoader::WriteCacheToFile(const std::string& uri, const std::vector<uint8_t>& imageData)
853 {
854 BackgroundTaskExecutor::GetInstance().PostTask(
855 [uri, data = std::move(imageData)]() {
856 ImageFileCache::GetInstance().WriteCacheFile(uri, data.data(), data.size());
857 },
858 BgTaskPriority::LOW);
859 }
860
WriteCacheToFile(const std::string & uri,const std::string & imageData)861 void ImageLoader::WriteCacheToFile(const std::string& uri, const std::string& imageData)
862 {
863 BackgroundTaskExecutor::GetInstance().PostTask(
864 [uri, data = std::move(imageData)]() {
865 ImageFileCache::GetInstance().WriteCacheFile(uri, data.data(), data.size());
866 },
867 BgTaskPriority::LOW);
868 }
869 } // namespace OHOS::Ace
870