Trigger svg loading early
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-url.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // CLASS HEADER
18 #include <dali-toolkit/internal/visuals/visual-url.h>
19
20 // EXTERNAL HEADERS
21 #include <dali/devel-api/common/hash.h>
22 #include <cstring> // for toupper()
23
24 namespace Dali
25 {
26 namespace Toolkit
27 {
28 namespace Internal
29 {
30 namespace
31 {
32 VisualUrl::ProtocolType ResolveLocation(const std::string& url)
33 {
34   const char*    urlCStr = url.c_str();
35   const uint32_t length  = url.size();
36   if((length > 7) && urlCStr[5] == ':' && urlCStr[6] == '/' && urlCStr[7] == '/')
37   {
38     // https:// or enbuf://
39     const char hOre = tolower(urlCStr[0]);
40     const char tOrn = tolower(urlCStr[1]);
41     const char tOrb = tolower(urlCStr[2]);
42     const char pOru = tolower(urlCStr[3]);
43     const char sOrf = tolower(urlCStr[4]);
44     if(('h' == hOre) &&
45        ('t' == tOrn) &&
46        ('t' == tOrb) &&
47        ('p' == pOru) &&
48        ('s' == sOrf))
49     {
50       return VisualUrl::REMOTE;
51     }
52     if(('e' == hOre) &&
53        ('n' == tOrn) &&
54        ('b' == tOrb) &&
55        ('u' == pOru) &&
56        ('f' == sOrf))
57     {
58       return VisualUrl::BUFFER;
59     }
60   }
61   else if((length > 6) && urlCStr[4] == ':' && urlCStr[5] == '/' && urlCStr[6] == '/')
62   {
63     // http:// or dali://
64     const char hOrd = tolower(urlCStr[0]);
65     const char tOra = tolower(urlCStr[1]);
66     const char tOrl = tolower(urlCStr[2]);
67     const char pOri = tolower(urlCStr[3]);
68     if(('h' == hOrd) &&
69        ('t' == tOra) &&
70        ('t' == tOrl) &&
71        ('p' == pOri))
72     {
73       return VisualUrl::REMOTE;
74     }
75     if(('d' == hOrd) &&
76        ('a' == tOra) &&
77        ('l' == tOrl) &&
78        ('i' == pOri))
79     {
80       return VisualUrl::TEXTURE;
81     }
82   }
83   else if((length > 5) && urlCStr[3] == ':' && urlCStr[4] == '/' && urlCStr[5] == '/')
84   {
85     // ftp:// or ssh://
86     const char fOrs = tolower(urlCStr[0]);
87     const char tOrs = tolower(urlCStr[1]);
88     const char pOrh = tolower(urlCStr[2]);
89     if(('f' == fOrs) &&
90        ('t' == tOrs) &&
91        ('p' == pOrh))
92     {
93       return VisualUrl::REMOTE;
94     }
95     if(('s' == fOrs) &&
96        ('s' == tOrs) &&
97        ('h' == pOrh))
98     {
99       return VisualUrl::REMOTE;
100     }
101   }
102   return VisualUrl::LOCAL;
103 }
104
105 VisualUrl::Type ResolveType(const std::string& url)
106 {
107   // if only one char in string, can only be regular image
108   const std::size_t count      = url.size();
109   VisualUrl::Type   returnType = VisualUrl::REGULAR_IMAGE;
110   if(count > 0)
111   {
112     // parsing from the end for better chance of early outs
113     enum
114     {
115       SUFFIX,
116       HASH,
117       HASH_DOT
118     } state                = SUFFIX;
119     char         SVG[4]    = {'g', 'v', 's', '.'};
120     char         GIF[4]    = {'f', 'i', 'g', '.'};
121     char         WEBP[5]   = {'p', 'b', 'e', 'w', '.'};
122     char         JSON[5]   = {'n', 'o', 's', 'j', '.'};
123     char         TVG[4]    = {'g', 'v', 't', '.'};
124     unsigned int svgScore  = 0;
125     unsigned int tvgScore  = 0;
126     unsigned int gifScore  = 0;
127     unsigned int webpScore = 0;
128     unsigned int jsonScore = 0;
129     int          index     = count;
130     while(--index >= 0)
131     {
132       const char        currentChar   = tolower(url[index]);
133       const std::size_t offsetFromEnd = count - index - 1u;
134       if((offsetFromEnd < sizeof(SVG)) && (currentChar == SVG[offsetFromEnd]))
135       {
136         // early out if SVG as can't be used in N patch for now
137         if(++svgScore == sizeof(SVG))
138         {
139           return VisualUrl::SVG;
140         }
141       }
142       if((offsetFromEnd < sizeof(TVG)) && (currentChar == TVG[offsetFromEnd]))
143       {
144         // early out if TVG as can't be used in N patch for now
145         if(++tvgScore == sizeof(TVG))
146         {
147           return VisualUrl::TVG;
148         }
149       }
150       if((offsetFromEnd < sizeof(GIF)) && (currentChar == GIF[offsetFromEnd]))
151       {
152         //find type, but need to be check used in N patch
153         if(++gifScore == sizeof(GIF))
154         {
155           returnType = VisualUrl::GIF;
156         }
157       }
158       if((offsetFromEnd < sizeof(WEBP)) && (currentChar == WEBP[offsetFromEnd]))
159       {
160         if(++webpScore == sizeof(WEBP))
161         {
162           //find type, but need to be check used in N patch
163           returnType = VisualUrl::WEBP;
164         }
165       }
166       if((offsetFromEnd < sizeof(JSON)) && (currentChar == JSON[offsetFromEnd]))
167       {
168         // early out if JSON as can't be used in N patch for now
169         if(++jsonScore == sizeof(JSON))
170         {
171           return VisualUrl::JSON;
172         }
173       }
174       switch(state)
175       {
176         case SUFFIX:
177         {
178           if('.' == currentChar)
179           {
180             state = HASH;
181           }
182           break;
183         }
184         case HASH:
185         {
186           if(('#' == currentChar) || ('9' == currentChar))
187           {
188             state = HASH_DOT;
189           }
190           else
191           {
192             // early out, not a valid N/9-patch URL
193             return returnType;
194           }
195           break;
196         }
197         case HASH_DOT:
198         {
199           if('.' == currentChar)
200           {
201             return VisualUrl::N_PATCH;
202           }
203           else
204           {
205             // early out, not a valid N/9-patch URL
206             return returnType;
207           }
208           break;
209         }
210       }
211     }
212   }
213   // if we got here it is a regular image
214   return returnType;
215 }
216
217 } // namespace
218
219 VisualUrl::VisualUrl()
220 : mUrl(),
221   mType(VisualUrl::REGULAR_IMAGE),
222   mLocation(VisualUrl::LOCAL),
223   mUrlHash(0ull)
224 {
225 }
226
227 VisualUrl::VisualUrl(const std::string& url)
228 : mUrl(url),
229   mType(VisualUrl::REGULAR_IMAGE),
230   mLocation(VisualUrl::LOCAL),
231   mUrlHash(0ull)
232 {
233   if(!url.empty())
234   {
235     mLocation = ResolveLocation(url);
236     if(VisualUrl::TEXTURE != mLocation && VisualUrl::BUFFER != mLocation)
237     {
238       // TEXTURE and BUFFER location url doesn't need type resolving, REGULAR_IMAGE is fine
239       mType = ResolveType(url);
240     }
241   }
242 }
243
244 VisualUrl::VisualUrl(const VisualUrl& url)
245 : mUrl(url.mUrl),
246   mType(url.mType),
247   mLocation(url.mLocation),
248   mUrlHash(url.mUrlHash)
249 {
250 }
251
252 VisualUrl::~VisualUrl()
253 {
254 }
255
256 VisualUrl& VisualUrl::operator=(const VisualUrl& url)
257 {
258   if(&url != this)
259   {
260     mUrl      = url.mUrl;
261     mType     = url.mType;
262     mLocation = url.mLocation;
263     mUrlHash  = url.mUrlHash;
264   }
265   return *this;
266 }
267
268 const std::string& VisualUrl::GetUrl() const
269 {
270   return mUrl;
271 }
272
273 const std::uint64_t& VisualUrl::GetUrlHash() const
274 {
275   return DALI_UNLIKELY(mUrlHash == 0) ? (mUrlHash = Dali::CalculateHash(mUrl)) : mUrlHash;
276 }
277
278 VisualUrl::Type VisualUrl::GetType() const
279 {
280   return mType;
281 }
282
283 VisualUrl::ProtocolType VisualUrl::GetProtocolType() const
284 {
285   return mLocation;
286 }
287
288 bool VisualUrl::IsValid() const
289 {
290   return mUrl.size() > 0u;
291 }
292
293 bool VisualUrl::IsLocalResource() const
294 {
295   return mLocation == VisualUrl::LOCAL;
296 }
297
298 bool VisualUrl::IsBufferResource() const
299 {
300   return mLocation == VisualUrl::BUFFER;
301 }
302
303 std::string VisualUrl::GetLocation() const
304 {
305   return GetLocation(mUrl);
306 }
307
308 std::string VisualUrl::CreateTextureUrl(const std::string& location)
309 {
310   return "dali://" + location;
311 }
312
313 std::string VisualUrl::CreateBufferUrl(const std::string& location)
314 {
315   return "enbuf://" + location;
316 }
317
318 VisualUrl::ProtocolType VisualUrl::GetProtocolType(const std::string& url)
319 {
320   return ResolveLocation(url);
321 }
322
323 std::string VisualUrl::GetLocation(const std::string& url)
324 {
325   const auto location = url.find("://");
326   if(std::string::npos != location)
327   {
328     return url.substr(location + 3u); // 3 characters forwards from the start of ://
329   }
330   return url;
331 }
332
333 } // namespace Internal
334
335 } // namespace Toolkit
336
337 } // namespace Dali