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