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