1 /*
2  * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25 
26 #include <string.h>
27 
28 #include "tvgInlist.h"
29 #include "tvgLoader.h"
30 #include "tvgLock.h"
31 
32 #ifdef THORVG_SVG_LOADER_SUPPORT
33     #include "tvgSvgLoader.h"
34 #endif
35 
36 #ifdef THORVG_PNG_LOADER_SUPPORT
37     #include "tvgPngLoader.h"
38 #endif
39 
40 #ifdef THORVG_TVG_LOADER_SUPPORT
41     #include "tvgTvgLoader.h"
42 #endif
43 
44 #ifdef THORVG_JPG_LOADER_SUPPORT
45     #include "tvgJpgLoader.h"
46 #endif
47 
48 #ifdef THORVG_WEBP_LOADER_SUPPORT
49     #include "tvgWebpLoader.h"
50 #endif
51 
52 #ifdef THORVG_TTF_LOADER_SUPPORT
53     #include "tvgTtfLoader.h"
54 #endif
55 
56 #ifdef THORVG_LOTTIE_LOADER_SUPPORT
57     #include "tvgLottieLoader.h"
58 #endif
59 
60 #include "tvgRawLoader.h"
61 
62 
HASH_KEY(const char * data)63 uintptr_t HASH_KEY(const char* data)
64 {
65     return reinterpret_cast<uintptr_t>(data);
66 }
67 
68 /************************************************************************/
69 /* Internal Class Implementation                                        */
70 /************************************************************************/
71 
72 ColorSpace ImageLoader::cs = ColorSpace::ARGB8888;
73 
74 static Key key;
75 static Inlist<LoadModule> _activeLoaders;
76 
77 
_find(FileType type)78 static LoadModule* _find(FileType type)
79 {
80     switch(type) {
81         case FileType::Png: {
82 #ifdef THORVG_PNG_LOADER_SUPPORT
83             return new PngLoader;
84 #endif
85             break;
86         }
87         case FileType::Jpg: {
88 #ifdef THORVG_JPG_LOADER_SUPPORT
89             return new JpgLoader;
90 #endif
91             break;
92         }
93         case FileType::Webp: {
94 #ifdef THORVG_WEBP_LOADER_SUPPORT
95             return new WebpLoader;
96 #endif
97             break;
98         }
99         case FileType::Tvg: {
100 #ifdef THORVG_TVG_LOADER_SUPPORT
101             return new TvgLoader;
102 #endif
103             break;
104         }
105         case FileType::Svg: {
106 #ifdef THORVG_SVG_LOADER_SUPPORT
107             return new SvgLoader;
108 #endif
109             break;
110         }
111         case FileType::Ttf: {
112 #ifdef THORVG_TTF_LOADER_SUPPORT
113             return new TtfLoader;
114 #endif
115             break;
116         }
117         case FileType::Lottie: {
118 #ifdef THORVG_LOTTIE_LOADER_SUPPORT
119             return new LottieLoader;
120 #endif
121             break;
122         }
123         case FileType::Raw: {
124             return new RawLoader;
125             break;
126         }
127         default: {
128             break;
129         }
130     }
131 
132 #ifdef THORVG_LOG_ENABLED
133     const char *format;
134     switch(type) {
135         case FileType::Tvg: {
136             format = "TVG";
137             break;
138         }
139         case FileType::Svg: {
140             format = "SVG";
141             break;
142         }
143         case FileType::Ttf: {
144             format = "TTF";
145             break;
146         }
147         case FileType::Lottie: {
148             format = "lottie(json)";
149             break;
150         }
151         case FileType::Raw: {
152             format = "RAW";
153             break;
154         }
155         case FileType::Png: {
156             format = "PNG";
157             break;
158         }
159         case FileType::Jpg: {
160             format = "JPG";
161             break;
162         }
163         case FileType::Webp: {
164             format = "WEBP";
165             break;
166         }
167         default: {
168             format = "???";
169             break;
170         }
171     }
172     TVGLOG("RENDERER", "%s format is not supported", format);
173 #endif
174     return nullptr;
175 }
176 
177 
_findByPath(const string & path)178 static LoadModule* _findByPath(const string& path)
179 {
180     auto ext = path.substr(path.find_last_of(".") + 1);
181     if (!ext.compare("tvg")) return _find(FileType::Tvg);
182     if (!ext.compare("svg")) return _find(FileType::Svg);
183     if (!ext.compare("json")) return _find(FileType::Lottie);
184     if (!ext.compare("png")) return _find(FileType::Png);
185     if (!ext.compare("jpg")) return _find(FileType::Jpg);
186     if (!ext.compare("webp")) return _find(FileType::Webp);
187     if (!ext.compare("ttf") || !ext.compare("ttc")) return _find(FileType::Ttf);
188     if (!ext.compare("otf") || !ext.compare("otc")) return _find(FileType::Ttf);
189     return nullptr;
190 }
191 
192 
_convert(const string & mimeType)193 static FileType _convert(const string& mimeType)
194 {
195     auto type = FileType::Unknown;
196 
197     if (mimeType == "tvg") type = FileType::Tvg;
198     else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
199     else if (mimeType == "ttf" || mimeType == "otf") type = FileType::Ttf;
200     else if (mimeType == "lottie") type = FileType::Lottie;
201     else if (mimeType == "raw") type = FileType::Raw;
202     else if (mimeType == "png") type = FileType::Png;
203     else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
204     else if (mimeType == "webp") type = FileType::Webp;
205     else TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
206 
207     return type;
208 }
209 
210 
_findByType(const string & mimeType)211 static LoadModule* _findByType(const string& mimeType)
212 {
213     return _find(_convert(mimeType));
214 }
215 
216 
_findFromCache(const string & path)217 static LoadModule* _findFromCache(const string& path)
218 {
219     ScopedLock lock(key);
220 
221     auto loader = _activeLoaders.head;
222 
223     while (loader) {
224         if (loader->pathcache && !strcmp(loader->hashpath, path.c_str())) {
225             ++loader->sharing;
226             return loader;
227         }
228         loader = loader->next;
229     }
230     return nullptr;
231 }
232 
233 
_findFromCache(const char * data,uint32_t size,const string & mimeType)234 static LoadModule* _findFromCache(const char* data, uint32_t size, const string& mimeType)
235 {
236     auto type = _convert(mimeType);
237     if (type == FileType::Unknown) return nullptr;
238 
239     ScopedLock lock(key);
240     auto loader = _activeLoaders.head;
241 
242     auto key = HASH_KEY(data);
243 
244     while (loader) {
245         if (loader->type == type && loader->hashkey == key) {
246             ++loader->sharing;
247             return loader;
248         }
249         loader = loader->next;
250     }
251     return nullptr;
252 }
253 
254 
255 /************************************************************************/
256 /* External Class Implementation                                        */
257 /************************************************************************/
258 
259 
init()260 bool LoaderMgr::init()
261 {
262     return true;
263 }
264 
265 
term()266 bool LoaderMgr::term()
267 {
268     auto loader = _activeLoaders.head;
269 
270     //clean up the remained font loaders which is globally used.
271     while (loader && loader->type == FileType::Ttf) {
272         auto ret = loader->close();
273         auto tmp = loader;
274         loader = loader->next;
275         _activeLoaders.remove(tmp);
276         if (ret) delete(tmp);
277     }
278     return true;
279 }
280 
281 
retrieve(LoadModule * loader)282 bool LoaderMgr::retrieve(LoadModule* loader)
283 {
284     if (!loader) return false;
285     if (loader->close()) {
286         if (loader->cached()) {
287             ScopedLock lock(key);
288             _activeLoaders.remove(loader);
289         }
290         delete(loader);
291     }
292     return true;
293 }
294 
295 
loader(const string & path,bool * invalid)296 LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
297 {
298     *invalid = false;
299 
300     //TODO: svg & lottie is not sharable.
301     auto allowCache = true;
302     auto ext = path.substr(path.find_last_of(".") + 1);
303     if (!ext.compare("svg") || !ext.compare("json")) allowCache = false;
304 
305     if (allowCache) {
306         if (auto loader = _findFromCache(path)) return loader;
307     }
308 
309     if (auto loader = _findByPath(path)) {
310         if (loader->open(path)) {
311             if (allowCache) {
312                 loader->hashpath = strdup(path.c_str());
313                 loader->pathcache = true;
314                 {
315                     ScopedLock lock(key);
316                     _activeLoaders.back(loader);
317                 }
318             }
319             return loader;
320         }
321         delete(loader);
322     }
323     //Unknown MimeType. Try with the candidates in the order
324     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
325         if (auto loader = _find(static_cast<FileType>(i))) {
326             if (loader->open(path)) {
327                 if (allowCache) {
328                     loader->hashpath = strdup(path.c_str());
329                     loader->pathcache = true;
330                     {
331                         ScopedLock lock(key);
332                         _activeLoaders.back(loader);
333                     }
334                 }
335                 return loader;
336             }
337             delete(loader);
338         }
339     }
340     *invalid = true;
341     return nullptr;
342 }
343 
344 
retrieve(const string & path)345 bool LoaderMgr::retrieve(const string& path)
346 {
347     return retrieve(_findFromCache(path));
348 }
349 
350 
loader(const char * key)351 LoadModule* LoaderMgr::loader(const char* key)
352 {
353     auto loader = _activeLoaders.head;
354 
355     while (loader) {
356         if (loader->pathcache && strstr(loader->hashpath, key)) {
357             ++loader->sharing;
358             return loader;
359         }
360         loader = loader->next;
361     }
362     return nullptr;
363 }
364 
365 
loader(const char * data,uint32_t size,const string & mimeType,bool copy)366 LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
367 {
368     //Note that users could use the same data pointer with the different content.
369     //Thus caching is only valid for shareable.
370     auto allowCache = !copy;
371 
372     //TODO: lottie is not sharable.
373     if (allowCache) {
374         auto type = _convert(mimeType);
375         if (type == FileType::Lottie) allowCache = false;
376     }
377 
378     if (allowCache) {
379         if (auto loader = _findFromCache(data, size, mimeType)) return loader;
380     }
381 
382     //Try with the given MimeType
383     if (!mimeType.empty()) {
384         if (auto loader = _findByType(mimeType)) {
385             if (loader->open(data, size, copy)) {
386                 if (allowCache) {
387                     loader->hashkey = HASH_KEY(data);
388                     ScopedLock lock(key);
389                     _activeLoaders.back(loader);
390                 }
391                 return loader;
392             } else {
393                 TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType.c_str());
394                 delete(loader);
395             }
396         }
397     }
398     //Unknown MimeType. Try with the candidates in the order
399     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
400         auto loader = _find(static_cast<FileType>(i));
401         if (loader) {
402             if (loader->open(data, size, copy)) {
403                 if (allowCache) {
404                     loader->hashkey = HASH_KEY(data);
405                     ScopedLock lock(key);
406                     _activeLoaders.back(loader);
407                 }
408                 return loader;
409             }
410             delete(loader);
411         }
412     }
413     return nullptr;
414 }
415 
416 
loader(const uint32_t * data,uint32_t w,uint32_t h,bool copy)417 LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
418 {
419     //Note that users could use the same data pointer with the different content.
420     //Thus caching is only valid for shareable.
421     if (!copy) {
422         //TODO: should we check premultiplied??
423         if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader;
424     }
425 
426     //function is dedicated for raw images only
427     auto loader = new RawLoader;
428     if (loader->open(data, w, h, copy)) {
429         if (!copy) {
430             loader->hashkey = HASH_KEY((const char*)data);
431             ScopedLock lock(key);
432             _activeLoaders.back(loader);
433         }
434         return loader;
435     }
436     delete(loader);
437     return nullptr;
438 }
439 
440 
441 //loads fonts from memory - loader is cached (regardless of copy value) in order to access it while setting font
loader(const char * name,const char * data,uint32_t size,TVG_UNUSED const string & mimeType,bool copy)442 LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size, TVG_UNUSED const string& mimeType, bool copy)
443 {
444 #ifdef THORVG_TTF_LOADER_SUPPORT
445     //TODO: add check for mimetype ?
446     if (auto loader = _findFromCache(name)) return loader;
447 
448     //function is dedicated for ttf loader (the only supported font loader)
449     auto loader = new TtfLoader;
450     if (loader->open(data, size, copy)) {
451         loader->hashpath = strdup(name);
452         loader->pathcache = true;
453         ScopedLock lock(key);
454         _activeLoaders.back(loader);
455         return loader;
456     }
457 
458     TVGLOG("LOADER", "The font data \"%s\" could not be loaded.", name);
459     delete(loader);
460 #endif
461     return nullptr;
462 }
463 
464 #endif /* LV_USE_THORVG_INTERNAL */
465 
466