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 <regex>
19
20 #include "core/common/container.h"
21
22 namespace OHOS::Ace {
23 namespace {
24
25 constexpr uint32_t FILE_SUFFIX_LEN = 4;
26 constexpr uint32_t APNG_FILE_SUFFIX_LEN = 5;
27
28 } // namespace
29
IsSVGSource(const std::string & src,InternalResource::ResourceId resourceId)30 bool ImageSourceInfo::IsSVGSource(const std::string& src, InternalResource::ResourceId resourceId)
31 {
32 // 4 is the length of ".svg".
33 return (src.size() > FILE_SUFFIX_LEN && src.substr(src.size() - FILE_SUFFIX_LEN) == ".svg") ||
34 (src.empty() && resourceId > InternalResource::ResourceId::SVG_START &&
35 resourceId < InternalResource::ResourceId::SVG_END);
36 }
37
IsPngSource(const std::string & src,InternalResource::ResourceId resourceId)38 bool ImageSourceInfo::IsPngSource(const std::string& src, InternalResource::ResourceId resourceId)
39 {
40 // 4 is the length of ".png" or is .apng
41 if (!src.empty()) {
42 std::string head = src.size() > APNG_FILE_SUFFIX_LEN
43 ? src.substr(src.size() - APNG_FILE_SUFFIX_LEN, APNG_FILE_SUFFIX_LEN)
44 : src.size() == 4 ? src.substr(src.size() - FILE_SUFFIX_LEN, FILE_SUFFIX_LEN)
45 : "";
46 std::transform(head.begin(), head.end(), head.begin(), [](unsigned char c) { return std::tolower(c); });
47
48 return (head.size() > FILE_SUFFIX_LEN && head.substr(head.size() - FILE_SUFFIX_LEN) == ".png") ||
49 (head.size() > APNG_FILE_SUFFIX_LEN && head.substr(head.size() - APNG_FILE_SUFFIX_LEN) == ".apng");
50 } else if (resourceId < InternalResource::ResourceId::SVG_START) {
51 return true;
52 }
53 return false;
54 }
55
IsValidBase64Head(const std::string & uri,const std::string & pattern)56 bool ImageSourceInfo::IsValidBase64Head(const std::string& uri, const std::string& pattern)
57 {
58 auto iter = uri.find_first_of(',');
59 if (iter == std::string::npos) {
60 LOGE("wrong base64 head format.");
61 return false;
62 }
63 std::string base64Head = uri.substr(0, iter);
64 std::regex regular(pattern);
65 return std::regex_match(base64Head, regular);
66 }
67
IsUriOfDataAbilityEncoded(const std::string & uri,const std::string & pattern)68 bool ImageSourceInfo::IsUriOfDataAbilityEncoded(const std::string& uri, const std::string& pattern)
69 {
70 std::regex regular(pattern);
71 return std::regex_match(uri, regular);
72 }
73
ResolveURIType(const std::string & uri)74 SrcType ImageSourceInfo::ResolveURIType(const std::string& uri)
75 {
76 if (uri.empty()) {
77 return SrcType::UNSUPPORTED;
78 }
79 auto iter = uri.find_first_of(':');
80 if (iter == std::string::npos) {
81 return SrcType::ASSET;
82 }
83 std::string head = uri.substr(0, iter);
84 std::transform(head.begin(), head.end(), head.begin(), [](unsigned char c) { return std::tolower(c); });
85 if (head == "http" || head == "https") {
86 return SrcType::NETWORK;
87 } else if (head == "file") {
88 if (IsUriOfDataAbilityEncoded(uri, "^file://media/.*thumbnail.*$")) {
89 return SrcType::DATA_ABILITY_DECODED;
90 } else if (IsUriOfDataAbilityEncoded(uri, "^file://media/.*")) {
91 return SrcType::DATA_ABILITY;
92 }
93 return SrcType::FILE;
94 } else if (head == "internal") {
95 return SrcType::INTERNAL;
96 } else if (head == "data") {
97 static constexpr char BASE64_PATTERN[] =
98 "^data:image/(jpeg|JPEG|jpg|JPG|png|PNG|ico|ICO|gif|GIF|bmp|BMP|webp|WEBP);base64$";
99 if (IsValidBase64Head(uri, BASE64_PATTERN)) {
100 return SrcType::BASE64;
101 }
102 return SrcType::UNSUPPORTED;
103 } else if (head == "memory") {
104 return SrcType::MEMORY;
105 } else if (head == "resource") {
106 return SrcType::RESOURCE;
107 } else if (head == "dataability" || head == "datashare") {
108 if (IsUriOfDataAbilityEncoded(uri, "^dataability://.*?/media/.*thumbnail.*$") ||
109 IsUriOfDataAbilityEncoded(uri, "^datashare://.*?/media/.*thumbnail.*$")) {
110 return SrcType::DATA_ABILITY_DECODED;
111 }
112 return SrcType::DATA_ABILITY;
113 } else {
114 return SrcType::UNSUPPORTED;
115 }
116 }
117
118 // 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)119 ImageSourceInfo::ImageSourceInfo(std::string imageSrc, std::string bundleName, std::string moduleName, Dimension width,
120 Dimension height, InternalResource::ResourceId resourceId, const RefPtr<PixelMap>& pixmap)
121 : src_(std::move(imageSrc)), bundleName_(std::move(bundleName)), moduleName_(std::move(moduleName)),
122 sourceWidth_(width), sourceHeight_(height), resourceId_(resourceId), pixmap_(pixmap),
123 isSvg_(IsSVGSource(src_, resourceId_)), isPng_(IsPngSource(src_, resourceId_)), srcType_(ResolveSrcType())
124 {
125 // count how many source set.
126 int32_t count = 0;
127 if (!src_.empty()) {
128 ++count;
129 }
130 if (resourceId_ != InternalResource::ResourceId::NO_ID) {
131 ++count;
132 }
133 if (pixmap != nullptr) {
134 ++count;
135 }
136 if (count > 1) {
137 LOGW("multi image source set, only one will be load.");
138 }
139 GenerateCacheKey();
140 }
141
ResolveSrcType() const142 SrcType ImageSourceInfo::ResolveSrcType() const
143 {
144 if (pixmap_) {
145 return SrcType::PIXMAP;
146 }
147 if (!src_.empty()) {
148 return ResolveURIType(src_);
149 }
150 if (resourceId_ != InternalResource::ResourceId::NO_ID) {
151 return SrcType::RESOURCE_ID;
152 }
153 return SrcType::UNSUPPORTED;
154 }
155
GenerateCacheKey()156 void ImageSourceInfo::GenerateCacheKey()
157 {
158 auto name = ToString() + AceApplicationInfo::GetInstance().GetAbilityName() + bundleName_ + moduleName_;
159 cacheKey_ = std::to_string(std::hash<std::string> {}(name)) + std::to_string(static_cast<int32_t>(resourceId_));
160 }
161
SetFillColor(const Color & color)162 void ImageSourceInfo::SetFillColor(const Color& color)
163 {
164 fillColor_.emplace(color.GetValue());
165 }
166
operator ==(const ImageSourceInfo & info) const167 bool ImageSourceInfo::operator==(const ImageSourceInfo& info) const
168 {
169 // only svg uses fillColor
170 if (isSvg_ && fillColor_ != info.fillColor_) {
171 return false;
172 }
173 return ((!pixmap_ && !info.pixmap_) || (pixmap_ && info.pixmap_ && pixmap_ == info.pixmap_)) &&
174 src_ == info.src_ && resourceId_ == info.resourceId_;
175 }
176
operator !=(const ImageSourceInfo & info) const177 bool ImageSourceInfo::operator!=(const ImageSourceInfo& info) const
178 {
179 return !(operator==(info));
180 }
181
SetSrc(const std::string & src,std::optional<Color> fillColor)182 void ImageSourceInfo::SetSrc(const std::string& src, std::optional<Color> fillColor)
183 {
184 src_ = src;
185 srcType_ = ResolveURIType(src_);
186 resourceId_ = InternalResource::ResourceId::NO_ID;
187 isSvg_ = IsSVGSource(src_, resourceId_);
188 fillColor_ = fillColor;
189 pixmap_ = nullptr;
190 GenerateCacheKey();
191 }
192
GetSrc() const193 const std::string& ImageSourceInfo::GetSrc() const
194 {
195 return src_;
196 }
197
SetResourceId(InternalResource::ResourceId id,std::optional<Color> fillColor)198 void ImageSourceInfo::SetResourceId(InternalResource::ResourceId id, std::optional<Color> fillColor)
199 {
200 resourceId_ = id;
201 srcType_ = SrcType::RESOURCE_ID;
202 src_.clear();
203 isSvg_ = IsSVGSource(src_, resourceId_);
204 fillColor_ = fillColor;
205 pixmap_ = nullptr;
206 GenerateCacheKey();
207 }
208
GetResourceId() const209 InternalResource::ResourceId ImageSourceInfo::GetResourceId() const
210 {
211 return resourceId_;
212 }
213
SetPixMap(const RefPtr<PixelMap> & pixmap,std::optional<Color> fillColor)214 void ImageSourceInfo::SetPixMap(const RefPtr<PixelMap>& pixmap, std::optional<Color> fillColor)
215 {
216 resourceId_ = InternalResource::ResourceId::NO_ID;
217 srcType_ = SrcType::PIXMAP;
218 src_.clear();
219 isSvg_ = IsSVGSource(src_, resourceId_);
220 fillColor_ = fillColor;
221 pixmap_ = pixmap;
222 }
223
SetBundleName(const std::string & bundleName)224 void ImageSourceInfo::SetBundleName(const std::string& bundleName)
225 {
226 bundleName_ = bundleName;
227 }
228
SetModuleName(const std::string & moduleName)229 void ImageSourceInfo::SetModuleName(const std::string& moduleName)
230 {
231 moduleName_ = moduleName;
232 }
233
IsInternalResource() const234 bool ImageSourceInfo::IsInternalResource() const
235 {
236 return src_.empty() && resourceId_ != InternalResource::ResourceId::NO_ID && !pixmap_;
237 }
238
IsValid() const239 bool ImageSourceInfo::IsValid() const
240 {
241 return (src_.empty() && resourceId_ != InternalResource::ResourceId::NO_ID) ||
242 (!src_.empty() && resourceId_ == InternalResource::ResourceId::NO_ID) || pixmap_;
243 }
244
IsPng() const245 bool ImageSourceInfo::IsPng() const
246 {
247 return isPng_;
248 }
249
IsSvg() const250 bool ImageSourceInfo::IsSvg() const
251 {
252 return isSvg_;
253 }
254
IsPixmap() const255 bool ImageSourceInfo::IsPixmap() const
256 {
257 return pixmap_ != nullptr || SrcType::DATA_ABILITY_DECODED == srcType_;
258 }
259
GetBundleName() const260 const std::string& ImageSourceInfo::GetBundleName() const
261 {
262 return bundleName_;
263 }
264
GetModuleName() const265 const std::string& ImageSourceInfo::GetModuleName() const
266 {
267 return moduleName_;
268 }
269
GetSrcType() const270 SrcType ImageSourceInfo::GetSrcType() const
271 {
272 return srcType_;
273 }
274
ToString() const275 std::string ImageSourceInfo::ToString() const
276 {
277 if (!src_.empty()) {
278 return src_;
279 }
280 if (resourceId_ != InternalResource::ResourceId::NO_ID) {
281 return std::string("internal resource id: ") + std::to_string(static_cast<int32_t>(resourceId_));
282 }
283 if (pixmap_) {
284 return std::string("pixmapID: ") + pixmap_->GetId() + std::string(" -> modifyID: ") + pixmap_->GetModifyId();
285 }
286 return std::string("empty source");
287 }
288
SetDimension(Dimension width,Dimension Height)289 void ImageSourceInfo::SetDimension(Dimension width, Dimension Height)
290 {
291 sourceWidth_ = width;
292 sourceHeight_ = Height;
293 }
294
IsSourceDimensionValid() const295 bool ImageSourceInfo::IsSourceDimensionValid() const
296 {
297 return sourceWidth_.IsValid() && sourceHeight_.IsValid();
298 }
299
GetSourceSize() const300 Size ImageSourceInfo::GetSourceSize() const
301 {
302 return Size(sourceWidth_.Value(), sourceHeight_.Value());
303 }
304
Reset()305 void ImageSourceInfo::Reset()
306 {
307 src_.clear();
308 sourceWidth_ = Dimension(-1);
309 sourceHeight_ = Dimension(-1);
310 resourceId_ = InternalResource::ResourceId::NO_ID;
311 isSvg_ = false;
312 fillColor_.reset();
313 pixmap_ = nullptr;
314 cacheKey_.clear();
315 }
316
GetFillColor() const317 const std::optional<Color>& ImageSourceInfo::GetFillColor() const
318 {
319 return fillColor_;
320 }
321
GetPixmap() const322 const RefPtr<PixelMap>& ImageSourceInfo::GetPixmap() const
323 {
324 return pixmap_;
325 }
326
SupportObjCache() const327 bool ImageSourceInfo::SupportObjCache() const
328 {
329 if (IsPixmap()) {
330 return false;
331 }
332 if (!needCache_) {
333 return false;
334 }
335 return !src_.empty() || resourceId_ != InternalResource::ResourceId::NO_ID;
336 }
337
GetKey() const338 std::string ImageSourceInfo::GetKey() const
339 {
340 // only svg sets fillColor
341 if (isSvg_ && fillColor_.has_value()) {
342 return cacheKey_ + fillColor_.value().ColorToString();
343 }
344 return cacheKey_;
345 }
346
347 } // namespace OHOS::Ace
348