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 return SrcType::FILE;
89 } else if (head == "internal") {
90 return SrcType::INTERNAL;
91 } else if (head == "data") {
92 static constexpr char BASE64_PATTERN[] =
93 "^data:image/(jpeg|JPEG|jpg|JPG|png|PNG|ico|ICO|gif|GIF|bmp|BMP|webp|WEBP);base64$";
94 if (IsValidBase64Head(uri, BASE64_PATTERN)) {
95 return SrcType::BASE64;
96 }
97 return SrcType::UNSUPPORTED;
98 } else if (head == "memory") {
99 return SrcType::MEMORY;
100 } else if (head == "resource") {
101 return SrcType::RESOURCE;
102 } else if (head == "dataability" || head == "datashare") {
103 if (IsUriOfDataAbilityEncoded(uri, "^dataability://.*?/media/.*/thumbnail/.*$") ||
104 IsUriOfDataAbilityEncoded(uri, "^datashare://.*?/media/.*/thumbnail/.*$")) {
105 return SrcType::DATA_ABILITY_DECODED;
106 }
107 return SrcType::DATA_ABILITY;
108 } else {
109 return SrcType::UNSUPPORTED;
110 }
111 }
112
ImageSourceInfo(std::string imageSrc,Dimension width,Dimension height,InternalResource::ResourceId resourceId,const RefPtr<PixelMap> & pixmap)113 ImageSourceInfo::ImageSourceInfo(std::string imageSrc, Dimension width, Dimension height,
114 InternalResource::ResourceId resourceId, const RefPtr<PixelMap>& pixmap)
115 : src_(std::move(imageSrc)), sourceWidth_(width), sourceHeight_(height), resourceId_(resourceId), pixmap_(pixmap),
116 isSvg_(IsSVGSource(src_, resourceId_)), isPng_(IsPngSource(src_, resourceId_)), srcType_(ResolveSrcType())
117 {
118 // count how many source set.
119 int32_t count = 0;
120 if (!src_.empty()) {
121 ++count;
122 }
123 if (resourceId_ != InternalResource::ResourceId::NO_ID) {
124 ++count;
125 }
126 if (pixmap != nullptr) {
127 ++count;
128 }
129 if (count > 1) {
130 LOGW("multi image source set, only one will be load.");
131 }
132 GenerateCacheKey();
133 }
134
ResolveSrcType() const135 SrcType ImageSourceInfo::ResolveSrcType() const
136 {
137 if (pixmap_) {
138 return SrcType::PIXMAP;
139 }
140 if (!src_.empty()) {
141 return ResolveURIType(src_);
142 }
143 if (resourceId_ != InternalResource::ResourceId::NO_ID) {
144 return SrcType::RESOURCE_ID;
145 }
146 return SrcType::UNSUPPORTED;
147 }
148
GenerateCacheKey()149 void ImageSourceInfo::GenerateCacheKey()
150 {
151 auto name = ToString() + AceApplicationInfo::GetInstance().GetAbilityName();
152 cacheKey_ = std::to_string(std::hash<std::string> {}(name)) + std::to_string(static_cast<int32_t>(resourceId_));
153 }
154
SetFillColor(const Color & color)155 void ImageSourceInfo::SetFillColor(const Color& color)
156 {
157 fillColor_.emplace(color.GetValue());
158 }
159
operator ==(const ImageSourceInfo & info) const160 bool ImageSourceInfo::operator==(const ImageSourceInfo& info) const
161 {
162 // only svg uses fillColor
163 if (isSvg_ && fillColor_ != info.fillColor_) {
164 return false;
165 }
166 return ((!pixmap_ && !info.pixmap_) || (pixmap_ && info.pixmap_ && pixmap_ == info.pixmap_)) &&
167 // TODO: Use GetModifyId to distinguish two PixelMap objects after Media provides it
168 src_ == info.src_ && resourceId_ == info.resourceId_;
169 }
170
operator !=(const ImageSourceInfo & info) const171 bool ImageSourceInfo::operator!=(const ImageSourceInfo& info) const
172 {
173 return !(operator==(info));
174 }
175
SetSrc(const std::string & src,std::optional<Color> fillColor)176 void ImageSourceInfo::SetSrc(const std::string& src, std::optional<Color> fillColor)
177 {
178 src_ = src;
179 srcType_ = ResolveURIType(src_);
180 resourceId_ = InternalResource::ResourceId::NO_ID;
181 isSvg_ = IsSVGSource(src_, resourceId_);
182 fillColor_ = fillColor;
183 pixmap_ = nullptr;
184 GenerateCacheKey();
185 }
186
GetSrc() const187 const std::string& ImageSourceInfo::GetSrc() const
188 {
189 return src_;
190 }
191
SetResourceId(InternalResource::ResourceId id,std::optional<Color> fillColor)192 void ImageSourceInfo::SetResourceId(InternalResource::ResourceId id, std::optional<Color> fillColor)
193 {
194 resourceId_ = id;
195 srcType_ = SrcType::RESOURCE_ID;
196 src_.clear();
197 isSvg_ = IsSVGSource(src_, resourceId_);
198 fillColor_ = fillColor;
199 pixmap_ = nullptr;
200 GenerateCacheKey();
201 }
202
GetResourceId() const203 InternalResource::ResourceId ImageSourceInfo::GetResourceId() const
204 {
205 return resourceId_;
206 }
207
SetPixMap(const RefPtr<PixelMap> & pixmap,std::optional<Color> fillColor)208 void ImageSourceInfo::SetPixMap(const RefPtr<PixelMap>& pixmap, std::optional<Color> fillColor)
209 {
210 resourceId_ = InternalResource::ResourceId::NO_ID;
211 srcType_ = SrcType::PIXMAP;
212 src_.clear();
213 isSvg_ = IsSVGSource(src_, resourceId_);
214 fillColor_ = fillColor;
215 pixmap_ = pixmap;
216 }
217
IsInternalResource() const218 bool ImageSourceInfo::IsInternalResource() const
219 {
220 return src_.empty() && resourceId_ != InternalResource::ResourceId::NO_ID && !pixmap_;
221 }
222
IsValid() const223 bool ImageSourceInfo::IsValid() const
224 {
225 return (src_.empty() && resourceId_ != InternalResource::ResourceId::NO_ID) ||
226 (!src_.empty() && resourceId_ == InternalResource::ResourceId::NO_ID) || pixmap_;
227 }
228
IsPng() const229 bool ImageSourceInfo::IsPng() const
230 {
231 return isPng_;
232 }
233
IsSvg() const234 bool ImageSourceInfo::IsSvg() const
235 {
236 return isSvg_;
237 }
238
IsPixmap() const239 bool ImageSourceInfo::IsPixmap() const
240 {
241 return pixmap_ != nullptr || SrcType::DATA_ABILITY_DECODED == srcType_;
242 }
243
GetSrcType() const244 SrcType ImageSourceInfo::GetSrcType() const
245 {
246 return srcType_;
247 }
248
ToString() const249 std::string ImageSourceInfo::ToString() const
250 {
251 if (!src_.empty()) {
252 return src_;
253 }
254 if (resourceId_ != InternalResource::ResourceId::NO_ID) {
255 return std::string("internal resource id: ") + std::to_string(static_cast<int32_t>(resourceId_));
256 }
257 if (pixmap_) {
258 return std::string("pixmapID: ") + pixmap_->GetId() + std::string(" -> modifyID: ") + pixmap_->GetModifyId();
259 }
260 return std::string("empty source");
261 }
262
SetDimension(Dimension width,Dimension Height)263 void ImageSourceInfo::SetDimension(Dimension width, Dimension Height)
264 {
265 sourceWidth_ = width;
266 sourceHeight_ = Height;
267 }
268
IsSourceDimensionValid() const269 bool ImageSourceInfo::IsSourceDimensionValid() const
270 {
271 return sourceWidth_.IsValid() && sourceHeight_.IsValid();
272 }
273
GetSourceSize() const274 Size ImageSourceInfo::GetSourceSize() const
275 {
276 return Size(sourceWidth_.Value(), sourceHeight_.Value());
277 }
278
Reset()279 void ImageSourceInfo::Reset()
280 {
281 src_.clear();
282 sourceWidth_ = Dimension(-1);
283 sourceHeight_ = Dimension(-1);
284 resourceId_ = InternalResource::ResourceId::NO_ID;
285 isSvg_ = false;
286 fillColor_.reset();
287 pixmap_ = nullptr;
288 cacheKey_.clear();
289 }
290
GetFillColor() const291 const std::optional<Color>& ImageSourceInfo::GetFillColor() const
292 {
293 return fillColor_;
294 }
295
GetPixmap() const296 const RefPtr<PixelMap>& ImageSourceInfo::GetPixmap() const
297 {
298 return pixmap_;
299 }
300
IsSupportCache() const301 bool ImageSourceInfo::IsSupportCache() const
302 {
303 if (IsPixmap()) {
304 return false;
305 }
306 return !src_.empty() || resourceId_ != InternalResource::ResourceId::NO_ID;
307 }
308
GetKey() const309 std::string ImageSourceInfo::GetKey() const
310 {
311 // only svg sets fillColor
312 if (isSvg_ && fillColor_.has_value()) {
313 return cacheKey_ + fillColor_.value().ColorToString();
314 }
315 return cacheKey_;
316 }
317
318 } // namespace OHOS::Ace
319