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