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_source_info.h"
17 
18 #include "core/components_ng/base/frame_node.h"
19 #include "core/pipeline_ng/pipeline_context.h"
20 
21 namespace OHOS::Ace {
22 namespace {
23 
24 constexpr uint32_t FILE_SUFFIX_LEN = 4;
25 constexpr uint32_t APNG_FILE_SUFFIX_LEN = 5;
26 
CheckSvgExtension(const std::string & src)27 bool CheckSvgExtension(const std::string& src)
28 {
29     if (src.size() <= FILE_SUFFIX_LEN) {
30         return false;
31     }
32     auto srcSuffix = src.substr(src.size() - FILE_SUFFIX_LEN);
33     StringUtils::TransformStrCase(srcSuffix, StringUtils::TEXT_CASE_LOWERCASE);
34     return srcSuffix == ".svg";
35 }
36 
37 } // namespace
38 
IsSVGSource(const std::string & src,SrcType srcType,InternalResource::ResourceId resourceId)39 bool ImageSourceInfo::IsSVGSource(const std::string& src, SrcType srcType, InternalResource::ResourceId resourceId)
40 {
41     // 4 is the length of ".svg".
42     if (CheckSvgExtension(src)) {
43         return true;
44     } else if (srcType == SrcType::NETWORK) {
45         size_t queryPos = src.find('?');
46         std::string cleanUrl = (queryPos != std::string::npos) ? src.substr(0, queryPos) : src;
47         if (CheckSvgExtension(cleanUrl)) {
48             return true;
49         }
50     }
51     return (src.empty() && resourceId > InternalResource::ResourceId::SVG_START &&
52             resourceId < InternalResource::ResourceId::SVG_END);
53 }
54 
IsPngSource(const std::string & src,InternalResource::ResourceId resourceId)55 bool ImageSourceInfo::IsPngSource(const std::string& src, InternalResource::ResourceId resourceId)
56 {
57     // 4 is the length of ".png" or is .apng
58     if (!src.empty()) {
59         std::string head = src.size() > APNG_FILE_SUFFIX_LEN
60                                ? src.substr(src.size() - APNG_FILE_SUFFIX_LEN, APNG_FILE_SUFFIX_LEN)
61                            : src.size() == 4 ? src.substr(src.size() - FILE_SUFFIX_LEN, FILE_SUFFIX_LEN)
62                                              : "";
63         std::transform(head.begin(), head.end(), head.begin(), [](unsigned char c) { return std::tolower(c); });
64 
65         return (head.size() > FILE_SUFFIX_LEN && head.substr(head.size() - FILE_SUFFIX_LEN) == ".png") ||
66                (head.size() > APNG_FILE_SUFFIX_LEN && head.substr(head.size() - APNG_FILE_SUFFIX_LEN) == ".apng");
67     } else if (resourceId < InternalResource::ResourceId::SVG_START) {
68         return true;
69     }
70     return false;
71 }
72 
IsValidBase64Head(const std::string & uri,const std::string & pattern)73 bool ImageSourceInfo::IsValidBase64Head(const std::string& uri, const std::string& pattern)
74 {
75     auto iter = uri.find_first_of(',');
76     if (iter == std::string::npos) {
77         TAG_LOGW(AceLogTag::ACE_IMAGE, "ImageSourceInfo: wrong base64 head format.");
78         return false;
79     }
80     std::string base64Head = uri.substr(0, iter);
81     std::regex regular(pattern);
82     return std::regex_match(base64Head, regular);
83 }
84 
IsUriOfDataAbilityEncoded(const std::string & uri,const std::string & pattern)85 bool ImageSourceInfo::IsUriOfDataAbilityEncoded(const std::string& uri, const std::string& pattern)
86 {
87     std::regex regular(pattern);
88     return std::regex_match(uri, regular);
89 }
90 
ResolveURIType(const std::string & uri)91 SrcType ImageSourceInfo::ResolveURIType(const std::string& uri)
92 {
93     if (uri.empty()) {
94         return SrcType::UNSUPPORTED;
95     }
96     auto iter = uri.find_first_of(':');
97     if (iter == std::string::npos) {
98         return SrcType::ASSET;
99     }
100     std::string head = uri.substr(0, iter);
101     std::transform(head.begin(), head.end(), head.begin(), [](unsigned char c) { return std::tolower(c); });
102     if (head == "http" || head == "https") {
103         return SrcType::NETWORK;
104     } else if (head == "file") {
105         if (IsUriOfDataAbilityEncoded(uri, "^file://media/.*thumbnail.*$")) {
106             return SrcType::DATA_ABILITY_DECODED;
107         } else if (IsUriOfDataAbilityEncoded(uri, "^file://media/.*astc.*$")) {
108             return SrcType::ASTC;
109         } else if (IsUriOfDataAbilityEncoded(uri, "^file://media/.*")) {
110             return SrcType::DATA_ABILITY;
111         }
112         return SrcType::FILE;
113     } else if (head == "internal") {
114         return SrcType::INTERNAL;
115     } else if (head == "data") {
116         static constexpr char BASE64_PATTERN[] =
117             "^data:image/(jpeg|JPEG|jpg|JPG|png|PNG|ico|ICO|gif|GIF|bmp|BMP|webp|WEBP|heic|heif|HEIF"
118             "|sut|astc);base64$";
119         if (IsValidBase64Head(uri, BASE64_PATTERN)) {
120             return SrcType::BASE64;
121         }
122         return SrcType::UNSUPPORTED;
123     } else if (head == "memory") {
124         return SrcType::MEMORY;
125     } else if (head == "resource") {
126         return SrcType::RESOURCE;
127     } else if (head == "dataability" || head == "datashare") {
128         if (IsUriOfDataAbilityEncoded(uri, "^dataability://.*?/media/.*thumbnail.*$") ||
129             IsUriOfDataAbilityEncoded(uri, "^datashare://.*?/media/.*thumbnail.*$")) {
130             return SrcType::DATA_ABILITY_DECODED;
131         }
132         return SrcType::DATA_ABILITY;
133     } else {
134         return SrcType::UNSUPPORTED;
135     }
136 }
137 
138 // add constructor method for decompressed hap
ImageSourceInfo(std::string imageSrc,std::string bundleName,std::string moduleName,Dimension width,Dimension height,InternalResource::ResourceId resourceId,const RefPtr<PixelMap> & pixmap)139 ImageSourceInfo::ImageSourceInfo(std::string imageSrc, std::string bundleName, std::string moduleName, Dimension width,
140     Dimension height, InternalResource::ResourceId resourceId, const RefPtr<PixelMap>& pixmap)
141     : src_(std::move(imageSrc)), bundleName_(std::move(bundleName)), moduleName_(std::move(moduleName)),
142       sourceWidth_(width), sourceHeight_(height), resourceId_(resourceId), pixmap_(pixmap),
143       isPng_(IsPngSource(src_, resourceId_)), srcType_(ResolveSrcType())
144 {
145     isSvg_ = IsSVGSource(src_, srcType_, resourceId_);
146     // count how many source set.
147     int32_t count = 0;
148     if (!src_.empty()) {
149         ++count;
150     }
151     if (resourceId_ != InternalResource::ResourceId::NO_ID) {
152         ++count;
153     }
154     if (pixmap != nullptr) {
155         pixmapBuffer_ = pixmap->GetPixels();
156         ++count;
157     }
158     if (count > 1) {
159         TAG_LOGW(AceLogTag::ACE_IMAGE, "ImageSourceInfo: multi image source set, only one will be load.");
160     }
161 
162     auto pipelineContext = NG::PipelineContext::GetCurrentContext();
163     if (pipelineContext) {
164         localColorMode_ = pipelineContext->GetLocalColorMode();
165     }
166 
167     GenerateCacheKey();
168 }
169 
ImageSourceInfo(const std::shared_ptr<std::string> & imageSrc,std::string bundleName,std::string moduleName,Dimension width,Dimension height,InternalResource::ResourceId resourceId,const RefPtr<PixelMap> & pixmap)170 ImageSourceInfo::ImageSourceInfo(const std::shared_ptr<std::string>& imageSrc, std::string bundleName,
171     std::string moduleName, Dimension width, Dimension height, InternalResource::ResourceId resourceId,
172     const RefPtr<PixelMap>& pixmap)
173     : srcRef_(imageSrc), bundleName_(std::move(bundleName)), moduleName_(std::move(moduleName)), sourceWidth_(width),
174       sourceHeight_(height), resourceId_(resourceId), pixmap_(pixmap), isPng_(IsPngSource(*srcRef_, resourceId_)),
175       srcType_(ResolveSrcType())
176 {
177     // count how many source set.
178     int32_t count = 0;
179     if (srcRef_ && !(*srcRef_).empty()) {
180         isSvg_ = IsSVGSource((*srcRef_), srcType_, resourceId_);
181         ++count;
182     } else {
183         isSvg_ = IsSVGSource("", srcType_, resourceId_);
184     }
185     if (resourceId_ != InternalResource::ResourceId::NO_ID) {
186         ++count;
187     }
188     if (pixmap != nullptr) {
189         pixmapBuffer_ = pixmap->GetPixels();
190         ++count;
191     }
192     if (count > 1) {
193         TAG_LOGW(AceLogTag::ACE_IMAGE, "ImageSourceInfo: multi image source set, only one will be load.");
194     }
195 
196     auto pipelineContext = NG::PipelineContext::GetCurrentContext();
197     if (pipelineContext) {
198         localColorMode_ = pipelineContext->GetLocalColorMode();
199     }
200 
201     GenerateCacheKey();
202 }
203 
ResolveSrcType() const204 SrcType ImageSourceInfo::ResolveSrcType() const
205 {
206     if (pixmap_) {
207         return SrcType::PIXMAP;
208     }
209     auto& src = GetSrc();
210     if (!src.empty()) {
211         return ResolveURIType(src);
212     }
213     if (resourceId_ != InternalResource::ResourceId::NO_ID) {
214         return SrcType::RESOURCE_ID;
215     }
216     return SrcType::UNSUPPORTED;
217 }
218 
GenerateCacheKey()219 void ImageSourceInfo::GenerateCacheKey()
220 {
221     auto name = ToString();
222     name.append(AceApplicationInfo::GetInstance().GetAbilityName())
223         .append(bundleName_)
224         .append(moduleName_)
225         .append(std::to_string(static_cast<int32_t>(resourceId_)))
226         .append(std::to_string(static_cast<int32_t>(SystemProperties::GetColorMode())))
227         .append(std::to_string(static_cast<int32_t>(localColorMode_)));
228     if (srcType_ == SrcType::BASE64) {
229         name.append("SrcType:BASE64");
230     }
231     cacheKey_ = std::to_string(std::hash<std::string> {}(name));
232 }
233 
SetFillColor(const Color & color)234 void ImageSourceInfo::SetFillColor(const Color& color)
235 {
236     fillColor_.emplace(color.GetValue());
237 }
238 
operator ==(const ImageSourceInfo & info) const239 bool ImageSourceInfo::operator==(const ImageSourceInfo& info) const
240 {
241     if (localColorMode_ != info.localColorMode_) {
242         return false;
243     }
244     // only svg uses fillColor
245     if (isSvg_ && fillColor_ != info.fillColor_) {
246         return false;
247     }
248     return ((!pixmap_ && !info.pixmap_) ||
249                (pixmap_ && info.pixmap_ && pixmapBuffer_ == info.pixmap_->GetPixels())) &&
250            GetSrc() == info.GetSrc() && resourceId_ == info.resourceId_;
251 }
252 
operator !=(const ImageSourceInfo & info) const253 bool ImageSourceInfo::operator!=(const ImageSourceInfo& info) const
254 {
255     return !(operator==(info));
256 }
257 
SetSrc(const std::string & src,std::optional<Color> fillColor)258 void ImageSourceInfo::SetSrc(const std::string& src, std::optional<Color> fillColor)
259 {
260     srcRef_.reset(new std::string(src));
261     srcType_ = ResolveURIType(src);
262     resourceId_ = InternalResource::ResourceId::NO_ID;
263     isSvg_ = IsSVGSource(src, srcType_, resourceId_);
264     fillColor_ = fillColor;
265     pixmap_ = nullptr;
266     GenerateCacheKey();
267 }
268 
GetSrc() const269 const std::string& ImageSourceInfo::GetSrc() const
270 {
271     if (srcRef_) {
272         return *srcRef_;
273     }
274     return src_;
275 }
276 
SetResourceId(InternalResource::ResourceId id,std::optional<Color> fillColor)277 void ImageSourceInfo::SetResourceId(InternalResource::ResourceId id, std::optional<Color> fillColor)
278 {
279     resourceId_ = id;
280     srcType_ = SrcType::RESOURCE_ID;
281     src_.clear();
282     isSvg_ = IsSVGSource(src_, srcType_, resourceId_);
283     fillColor_ = fillColor;
284     pixmap_ = nullptr;
285     GenerateCacheKey();
286 }
287 
GetResourceId() const288 InternalResource::ResourceId ImageSourceInfo::GetResourceId() const
289 {
290     return resourceId_;
291 }
292 
SetPixMap(const RefPtr<PixelMap> & pixmap,std::optional<Color> fillColor)293 void ImageSourceInfo::SetPixMap(const RefPtr<PixelMap>& pixmap, std::optional<Color> fillColor)
294 {
295     resourceId_ = InternalResource::ResourceId::NO_ID;
296     srcType_ = SrcType::PIXMAP;
297     src_.clear();
298     srcRef_.reset();
299     isSvg_ = IsSVGSource(src_, srcType_, resourceId_);
300     fillColor_ = fillColor;
301     pixmap_ = pixmap;
302 }
303 
SetBundleName(const std::string & bundleName)304 void ImageSourceInfo::SetBundleName(const std::string& bundleName)
305 {
306     bundleName_ = bundleName;
307 }
308 
SetModuleName(const std::string & moduleName)309 void ImageSourceInfo::SetModuleName(const std::string& moduleName)
310 {
311     moduleName_ = moduleName;
312 }
313 
IsInternalResource() const314 bool ImageSourceInfo::IsInternalResource() const
315 {
316     return GetSrc().empty() && resourceId_ != InternalResource::ResourceId::NO_ID && !pixmap_;
317 }
318 
IsValid() const319 bool ImageSourceInfo::IsValid() const
320 {
321     auto& src = GetSrc();
322     return (src.empty() && resourceId_ != InternalResource::ResourceId::NO_ID) ||
323            (!src.empty() && resourceId_ == InternalResource::ResourceId::NO_ID) || pixmap_;
324 }
325 
IsPng() const326 bool ImageSourceInfo::IsPng() const
327 {
328     return isPng_;
329 }
330 
IsSvg() const331 bool ImageSourceInfo::IsSvg() const
332 {
333     return isSvg_ && !IsPixmap();
334 }
335 
IsPixmap() const336 bool ImageSourceInfo::IsPixmap() const
337 {
338     return pixmap_ != nullptr || SrcType::DATA_ABILITY_DECODED == srcType_ || SrcType::ASTC == srcType_;
339 }
340 
GetBundleName() const341 const std::string& ImageSourceInfo::GetBundleName() const
342 {
343     return bundleName_;
344 }
345 
GetModuleName() const346 const std::string& ImageSourceInfo::GetModuleName() const
347 {
348     return moduleName_;
349 }
350 
GetSrcType() const351 SrcType ImageSourceInfo::GetSrcType() const
352 {
353     return srcType_;
354 }
355 
ToString() const356 std::string ImageSourceInfo::ToString() const
357 {
358     auto& src = GetSrc();
359     if (!src.empty()) {
360         return src;
361     }
362     if (resourceId_ != InternalResource::ResourceId::NO_ID) {
363         return std::string("internal resource id: ") + std::to_string(static_cast<int32_t>(resourceId_));
364     }
365     if (pixmap_) {
366         int32_t w = pixmap_->GetWidth();
367         int32_t h = pixmap_->GetHeight();
368         int32_t totalSize = pixmap_->GetByteCount();
369         auto rowStride = pixmap_->GetRowStride();
370         return std::string("pixmapID: ") + pixmap_->GetId() + std::string(" -> modifyID: ") + pixmap_->GetModifyId() +
371                "details: _w" + std::to_string(w) + "_h" + std::to_string(h) + "_rowStride" + std::to_string(rowStride) +
372                "_byteCount" + std::to_string(totalSize);
373     }
374     return std::string("empty source");
375 }
376 
SetDimension(Dimension width,Dimension Height)377 void ImageSourceInfo::SetDimension(Dimension width, Dimension Height)
378 {
379     sourceWidth_ = width;
380     sourceHeight_ = Height;
381 }
382 
IsSourceDimensionValid() const383 bool ImageSourceInfo::IsSourceDimensionValid() const
384 {
385     return sourceWidth_.IsValid() && sourceHeight_.IsValid();
386 }
387 
GetSourceSize() const388 Size ImageSourceInfo::GetSourceSize() const
389 {
390     return Size(sourceWidth_.Value(), sourceHeight_.Value());
391 }
392 
Reset()393 void ImageSourceInfo::Reset()
394 {
395     srcRef_.reset();
396     src_.clear();
397     sourceWidth_ = Dimension(-1);
398     sourceHeight_ = Dimension(-1);
399     resourceId_ = InternalResource::ResourceId::NO_ID;
400     isSvg_ = false;
401     fillColor_.reset();
402     pixmap_ = nullptr;
403     cacheKey_.clear();
404 }
405 
GetFillColor() const406 const std::optional<Color>& ImageSourceInfo::GetFillColor() const
407 {
408     return fillColor_;
409 }
410 
GetPixmap() const411 const RefPtr<PixelMap>& ImageSourceInfo::GetPixmap() const
412 {
413     return pixmap_;
414 }
415 
SupportObjCache() const416 bool ImageSourceInfo::SupportObjCache() const
417 {
418     if (IsPixmap()) {
419         return false;
420     }
421     if (!needCache_) {
422         return false;
423     }
424     return !GetSrc().empty() || resourceId_ != InternalResource::ResourceId::NO_ID;
425 }
426 
GetKey() const427 std::string ImageSourceInfo::GetKey() const
428 {
429     // only svg sets fillColor
430     if (isSvg_ && fillColor_.has_value()) {
431         return cacheKey_ + fillColor_.value().ColorToString();
432     }
433     return cacheKey_;
434 }
435 
CreateImageSourceInfoWithHost(const RefPtr<NG::FrameNode> & host)436 ImageSourceInfo ImageSourceInfo::CreateImageSourceInfoWithHost(const RefPtr<NG::FrameNode>& host)
437 {
438     ImageSourceInfo imageSourceInfo;
439     CHECK_NULL_RETURN(host, imageSourceInfo);
440     auto context = host->GetContext();
441     CHECK_NULL_RETURN(context, imageSourceInfo);
442 
443     std::string bundleName = context->GetBundleName();
444     std::string moduleName = context->GetModuleName();
445     imageSourceInfo.SetBundleName(bundleName);
446     imageSourceInfo.SetModuleName(moduleName);
447 
448     return imageSourceInfo;
449 }
450 } // namespace OHOS::Ace
451