• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 <regex>
19 
20 #include "third_party/skia/include/codec/SkCodec.h"
21 #include "third_party/skia/include/utils/SkBase64.h"
22 
23 #include "base/network/download_manager.h"
24 #include "base/resource/ace_res_config.h"
25 #include "base/resource/asset_manager.h"
26 #include "base/thread/background_task_executor.h"
27 #include "base/utils/string_utils.h"
28 #include "core/common/ace_application_info.h"
29 #include "core/common/ace_engine.h"
30 #include "core/image/image_cache.h"
31 
32 namespace OHOS::Ace {
33 namespace {
34 
35 constexpr size_t FILE_HEAD_LENGTH = 7;           // 7 is the size of "file://"
36 constexpr size_t MEMORY_HEAD_LENGTH = 9;         // 9 is the size of "memory://"
37 constexpr size_t INTERNAL_FILE_HEAD_LENGTH = 15; // 15 is the size of "internal://app/"
38 // regex for "resource://colormode/xxx.type", colormode can be "light" or "dark", xxx represents system resource id,
39 // type can be "jpg", "png", "svg" and so on.
40 const std::regex MEDIA_RES_ID_REGEX(R"(^resource://\w+/([0-9]+)\.\w+$)", std::regex::icase);
41 const std::regex MEDIA_APP_RES_PATH_REGEX(R"(^resource://RAWFILE/(.*)$)");
42 const std::regex MEDIA_APP_RES_ID_REGEX(R"(^resource://.*/([0-9]+)\.\w+$)", std::regex::icase);
43 constexpr uint32_t MEDIA_RESOURCE_MATCH_SIZE = 2;
44 
45 #ifdef WINDOWS_PLATFORM
realpath(const char * path,char * resolved_path)46 char* realpath(const char* path, char* resolved_path)
47 {
48     if (strcpy_s(resolved_path, PATH_MAX, path) != 0) {
49         return nullptr;
50     }
51     return resolved_path;
52 }
53 #endif
54 
55 } // namespace
56 
RemovePathHead(const std::string & uri)57 std::string ImageLoader::RemovePathHead(const std::string& uri)
58 {
59     auto iter = uri.find_first_of(':');
60     if (iter == std::string::npos) {
61         LOGW("No scheme, not a File or Memory path! The path is %{private}s", uri.c_str());
62         return std::string();
63     }
64     std::string head = uri.substr(0, iter);
65     if ((head == "file" && uri.size() > FILE_HEAD_LENGTH) || (head == "memory" && uri.size() > MEMORY_HEAD_LENGTH) ||
66         (head == "internal" && uri.size() > INTERNAL_FILE_HEAD_LENGTH)) {
67         // the file uri format is like "file:///data/data...",
68         // the memory uri format is like "memory://imagename.png" for example,
69         // iter + 3 to get the absolutely file path substring : "/data/data..." or the image name: "imagename.png"
70         return uri.substr(iter + 3);
71     }
72     LOGE("Wrong scheme, not a File!");
73     return std::string();
74 }
75 
CreateImageLoader(const ImageSourceInfo & imageSourceInfo)76 RefPtr<ImageLoader> ImageLoader::CreateImageLoader(const ImageSourceInfo& imageSourceInfo)
77 {
78     SrcType srcType = imageSourceInfo.GetSrcType();
79     switch (srcType) {
80         case SrcType::INTERNAL:
81         case SrcType::FILE: {
82             return MakeRefPtr<FileImageLoader>();
83         }
84         case SrcType::NETWORK: {
85             return MakeRefPtr<NetworkImageLoader>();
86         }
87         case SrcType::ASSET: {
88             return MakeRefPtr<AssetImageLoader>();
89         }
90         case SrcType::BASE64: {
91             return MakeRefPtr<Base64ImageLoader>();
92         }
93         case SrcType::RESOURCE: {
94             return MakeRefPtr<ResourceImageLoader>();
95         }
96         case SrcType::DATA_ABILITY: {
97             return MakeRefPtr<DataProviderImageLoader>();
98         }
99         case SrcType::MEMORY: {
100             LOGE("Image source type: shared memory. image data is not come from image loader.");
101             return nullptr;
102         }
103         case SrcType::RESOURCE_ID: {
104             return MakeRefPtr<InternalImageLoader>();
105         }
106         default: {
107             LOGE("Image source type not supported!");
108             return nullptr;
109         }
110     }
111 }
112 
LoadDataFromCachedFile(const std::string & uri)113 sk_sp<SkData> ImageLoader::LoadDataFromCachedFile(const std::string& uri)
114 {
115     std::string cacheFilePath = ImageCache::GetImageCacheFilePath(uri);
116     if (cacheFilePath.length() > PATH_MAX) {
117         LOGE("cache file path is too long, cacheFilePath: %{private}s", cacheFilePath.c_str());
118         return nullptr;
119     }
120     bool cacheFileFound = ImageCache::GetFromCacheFile(cacheFilePath);
121     if (!cacheFileFound) {
122         return nullptr;
123     }
124     char realPath[PATH_MAX] = { 0x00 };
125     if (realpath(cacheFilePath.c_str(), realPath) == nullptr) {
126         LOGE("realpath fail! cacheFilePath: %{private}s, fail reason: %{public}s", cacheFilePath.c_str(),
127             strerror(errno));
128         return nullptr;
129     }
130     std::unique_ptr<FILE, decltype(&fclose)> file(fopen(realPath, "rb"), fclose);
131     if (file) {
132         return SkData::MakeFromFILE(file.get());
133     }
134     return nullptr;
135 }
136 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)137 sk_sp<SkData> FileImageLoader::LoadImageData(
138     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
139 {
140     auto src = imageSourceInfo.GetSrc();
141     std::string filePath = RemovePathHead(src);
142     if (imageSourceInfo.GetSrcType() == SrcType::INTERNAL) {
143         // the internal source uri format is like "internal://app/imagename.png", the absolute path of which is like
144         // "/data/data/{bundleName}/files/imagename.png"
145         auto bundleName = AceApplicationInfo::GetInstance().GetPackageName();
146         if (bundleName.empty()) {
147             LOGE("bundleName is empty, LoadImageData for internal source fail!");
148             return nullptr;
149         }
150         if (!StringUtils::StartWith(filePath, "app/")) { // "app/" is infix of internal path
151             LOGE("internal path format is wrong. path is %{private}s", src.c_str());
152             return nullptr;
153         }
154         filePath = std::string("/data/data/") // head of absolute path
155                        .append(bundleName)
156                        .append("/files/")           // infix of absolute path
157                        .append(filePath.substr(4)); // 4 is the length of "app/" from "internal://app/"
158     }
159     if (filePath.length() > PATH_MAX) {
160         LOGE("src path is too long");
161         return nullptr;
162     }
163     char realPath[PATH_MAX] = { 0x00 };
164     if (realpath(filePath.c_str(), realPath) == nullptr) {
165         LOGE("realpath fail! filePath: %{private}s, fail reason: %{public}s src:%{public}s", filePath.c_str(),
166             strerror(errno), src.c_str());
167         return nullptr;
168     }
169     std::unique_ptr<FILE, decltype(&fclose)> file(fopen(realPath, "rb"), fclose);
170     if (!file) {
171         LOGE("open file failed, filePath: %{private}s, fail reason: %{public}s", filePath.c_str(), strerror(errno));
172         return nullptr;
173     }
174     return SkData::MakeFromFILE(file.get());
175 }
176 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)177 sk_sp<SkData> DataProviderImageLoader::LoadImageData(
178     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
179 {
180     auto src = imageSourceInfo.GetSrc();
181     auto skData = ImageLoader::LoadDataFromCachedFile(src);
182     if (skData) {
183         return skData;
184     }
185     auto pipeline = context.Upgrade();
186     if (!pipeline) {
187         LOGE("the pipeline context is null");
188         return nullptr;
189     }
190     auto dataProvider = pipeline->GetDataProviderManager();
191     if (!dataProvider) {
192         LOGE("the data provider is null");
193         return nullptr;
194     }
195     auto dataRes = dataProvider->GetDataProviderResFromUri(src);
196     if (!dataRes || dataRes->GetData().size() == 0) {
197         LOGE("fail to get data res is from data provider");
198         return nullptr;
199     }
200     auto imageData = dataRes->GetData();
201     sk_sp<SkData> data = SkData::MakeWithCopy(imageData.data(), imageData.size());
202     BackgroundTaskExecutor::GetInstance().PostTask(
203         [src, imgData = std::move(imageData)]() { ImageCache::WriteCacheFile(src, imgData.data(), imgData.size()); },
204         BgTaskPriority::LOW);
205     return data;
206 }
207 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)208 sk_sp<SkData> AssetImageLoader::LoadImageData(
209     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
210 {
211     auto src = imageSourceInfo.GetSrc();
212     if (src.empty()) {
213         LOGE("image src is empty");
214         return nullptr;
215     }
216 
217     std::string assetSrc(src);
218     if (assetSrc[0] == '/') {
219         assetSrc = assetSrc.substr(1); // get the asset src without '/'.
220     } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
221         assetSrc = assetSrc.substr(2); // get the asset src without './'.
222     }
223     auto pipelineContext = context.Upgrade();
224     if (!pipelineContext) {
225         LOGE("invalid pipeline context");
226         return nullptr;
227     }
228     auto assetManager = pipelineContext->GetAssetManager();
229     if (!assetManager) {
230         LOGE("No asset manager!");
231         return nullptr;
232     }
233     auto assetData = assetManager->GetAsset(assetSrc);
234     if (!assetData) {
235         LOGE("No asset data!");
236         return nullptr;
237     }
238     const uint8_t* data = assetData->GetData();
239     const size_t dataSize = assetData->GetSize();
240     return SkData::MakeWithCopy(data, dataSize);
241 }
242 
LoadJsonData(const std::string & src,const WeakPtr<PipelineContext> context)243 std::string AssetImageLoader::LoadJsonData(const std::string& src, const WeakPtr<PipelineContext> context)
244 {
245     if (src.empty()) {
246         LOGE("image src is empty");
247         return "";
248     }
249 
250     std::string assetSrc(src);
251     if (assetSrc[0] == '/') {
252         assetSrc = assetSrc.substr(1); // get the asset src without '/'.
253     } else if (assetSrc[0] == '.' && assetSrc.size() > 2 && assetSrc[1] == '/') {
254         assetSrc = assetSrc.substr(2); // get the asset src without './'.
255     }
256     auto pipelineContext = context.Upgrade();
257     if (!pipelineContext) {
258         LOGE("invalid pipeline context");
259         return "";
260     }
261     auto assetManager = pipelineContext->GetAssetManager();
262     if (!assetManager) {
263         LOGE("No asset manager!");
264         return "";
265     }
266     auto assetData = assetManager->GetAsset(assetSrc);
267     if (!assetData || !assetData->GetData()) {
268         LOGE("No asset data!");
269         return "";
270     }
271     return std::string((char *)assetData->GetData(), assetData->GetSize());
272 }
273 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)274 sk_sp<SkData> NetworkImageLoader::LoadImageData(
275     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
276 {
277     auto uri = imageSourceInfo.GetSrc();
278     // 1. find in cache file path.
279     auto skData = ImageLoader::LoadDataFromCachedFile(uri);
280     if (skData) {
281         return skData;
282     }
283 
284     // 2. if not found. download it.
285     std::vector<uint8_t> imageData;
286     if (!DownloadManager::GetInstance().Download(uri, imageData) || imageData.empty()) {
287         LOGE("Download image %{private}s failed!", uri.c_str());
288         return nullptr;
289     }
290     sk_sp<SkData> data = SkData::MakeWithCopy(imageData.data(), imageData.size());
291     // 3. write it into file cache.
292     BackgroundTaskExecutor::GetInstance().PostTask(
293         [uri, imgData = std::move(imageData)]() { ImageCache::WriteCacheFile(uri, imgData.data(), imgData.size()); },
294         BgTaskPriority::LOW);
295     return data;
296 }
297 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)298 sk_sp<SkData> InternalImageLoader::LoadImageData(
299     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
300 {
301     size_t imageSize = 0;
302     const uint8_t* internalData =
303         InternalResource::GetInstance().GetResource(imageSourceInfo.GetResourceId(), imageSize);
304     if (internalData == nullptr) {
305         LOGE("data null, the resource id may be wrong.");
306         return nullptr;
307     }
308     return SkData::MakeWithCopy(internalData, imageSize);
309 }
310 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)311 sk_sp<SkData> Base64ImageLoader::LoadImageData(
312     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
313 {
314     SkBase64 base64Decoder;
315     size_t imageSize = 0;
316     std::string base64Code = GetBase64ImageCode(imageSourceInfo.GetSrc(), imageSize);
317     SkBase64::Error error = base64Decoder.decode(base64Code.c_str(), base64Code.size());
318     if (error != SkBase64::kNoError) {
319         LOGE("error base64 image code!");
320         return nullptr;
321     }
322     auto base64Data = base64Decoder.getData();
323     const uint8_t* imageData = reinterpret_cast<uint8_t*>(base64Data);
324     auto resData = SkData::MakeWithCopy(imageData, imageSize);
325     // in SkBase64, the fData is not deleted after decoded.
326     if (base64Data != nullptr) {
327         delete[] base64Data;
328         base64Data = nullptr;
329     }
330     return resData;
331 }
332 
GetBase64ImageCode(const std::string & uri,size_t & imageSize)333 std::string Base64ImageLoader::GetBase64ImageCode(const std::string& uri, size_t& imageSize)
334 {
335     auto iter = uri.find_first_of(',');
336     if (iter == std::string::npos || iter == uri.size() - 1) {
337         LOGE("wrong code format!");
338         imageSize = 0;
339         return std::string();
340     }
341     // iter + 1 to skip the ","
342     std::string code = uri.substr(iter + 1);
343     imageSize = GetBase64ImageSize(code);
344     return code;
345 }
346 
GetBase64ImageSize(const std::string & code)347 size_t Base64ImageLoader::GetBase64ImageSize(const std::string& code)
348 {
349     // use base64 code size to calculate image byte size.
350     auto iter = code.rbegin();
351     int32_t count = 0;
352     // skip all '=' in the end.
353     while (*iter == '=') {
354         count++;
355         iter++;
356     }
357     // get the valid code length.
358     size_t codeSize = code.size() - count;
359     // compute the image byte size.
360     return codeSize - (codeSize / 8) * 2;
361 }
362 
GetResourceId(const std::string & uri,const RefPtr<ThemeConstants> & themeContants,uint32_t & resId) const363 bool ResourceImageLoader::GetResourceId(const std::string& uri, const RefPtr<ThemeConstants>& themeContants,
364     uint32_t& resId) const
365 {
366     std::smatch matches;
367     if (std::regex_match(uri, matches, MEDIA_RES_ID_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
368         resId = static_cast<uint32_t>(std::stoul(matches[1].str()));
369         return true;
370     }
371 
372     std::smatch appMatches;
373     if (std::regex_match(uri, appMatches, MEDIA_APP_RES_ID_REGEX) && appMatches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
374         resId = static_cast<uint32_t>(std::stoul(appMatches[1].str()));
375         return true;
376     }
377 
378     return false;
379 }
380 
GetResourceId(const std::string & uri,const RefPtr<ThemeConstants> & themeContants,std::string & path) const381 bool ResourceImageLoader::GetResourceId(const std::string& uri, const RefPtr<ThemeConstants>& themeContants,
382     std::string& path) const
383 {
384     std::smatch matches;
385     if (std::regex_match(uri, matches, MEDIA_APP_RES_PATH_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
386         path = matches[1].str();
387         return true;
388     }
389 
390     return false;
391 }
392 
LoadImageData(const ImageSourceInfo & imageSourceInfo,const WeakPtr<PipelineContext> context)393 sk_sp<SkData> ResourceImageLoader::LoadImageData(
394     const ImageSourceInfo& imageSourceInfo, const WeakPtr<PipelineContext> context)
395 {
396     auto uri = imageSourceInfo.GetSrc();
397     auto pipelineContext = context.Upgrade();
398     if (!pipelineContext) {
399         LOGE("invalid pipeline context");
400         return nullptr;
401     }
402     auto themeManager = pipelineContext->GetThemeManager();
403     if (!themeManager) {
404         LOGE("get theme manager failed");
405         return nullptr;
406     }
407     auto themeContants = themeManager->GetThemeConstants();
408     if (!themeContants) {
409         LOGE("get theme constants failed");
410         return nullptr;
411     }
412     uint32_t resId = 0;
413     std::string path;
414     std::ostringstream osstream;
415     if (GetResourceId(uri, themeContants, resId)) {
416         auto ret = themeContants->GetMediaResource(resId, osstream);
417         if (!ret) {
418             LOGE("get resId image(%{public}s) from resource manager failed", uri.c_str());
419             return nullptr;
420         }
421     } else if (GetResourceId(uri, themeContants, path)) {
422         auto ret = themeContants->GetMediaResource(path, osstream);
423         if (!ret) {
424             LOGE("get path image(%{public}s) from resource manager failed", uri.c_str());
425             return nullptr;
426         }
427     } else {
428         LOGE("get image resource id failed");
429         return nullptr;
430     }
431 
432     const auto& mediaRes = osstream.str();
433     return SkData::MakeWithCopy(mediaRes.c_str(), mediaRes.size());
434 }
435 
436 } // namespace OHOS::Ace