summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCedric BAIL <cedric.bail@samsung.com>2014-03-11 19:08:40 +0900
committerCedric BAIL <cedric.bail@free.fr>2014-04-01 22:00:13 +0900
commit961ecab040669ad2b181ece230e467ae708be8f8 (patch)
tree7f63ad9fffa7a36fec7e4c53c140b605f521e35c /src
parent4eb983614cf4d251426e2020ff17c3de403fa848 (diff)
evas: add a tgv loader.
The TGV file format is specifically created for Evas. It is designed to allow region decompression and parallele decompression with a fast path for GPU that do handle ETC1 compression. Plan for adding other compression method will come later.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile_Evas.am52
-rw-r--r--src/lib/evas/common/evas_image_load.c3
-rw-r--r--src/lib/evas/file/evas_module.c4
-rw-r--r--src/modules/evas/loaders/tgv/evas_image_load_tgv.c359
4 files changed, 418 insertions, 0 deletions
diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index 6132592ceb..5f07710a67 100644
--- a/src/Makefile_Evas.am
+++ b/src/Makefile_Evas.am
@@ -1712,6 +1712,58 @@ modules_evas_loaders_xpm_module_la_LIBTOOLFLAGS = --tag=disable-static
1712endif 1712endif
1713endif 1713endif
1714 1714
1715if BUILD_LOADER_TGV
1716if EVAS_STATIC_BUILD_TGV
1717lib_evas_libevas_la_SOURCES += \
1718modules/evas/loaders/tgv/evas_image_load_tgv.c \
1719static_libs/rg_etc/rg_etc1.c \
1720static_libs/rg_etc/rg_etc1.h \
1721static_libs/lz4/lz4.c \
1722static_libs/lz4/lz4.h
1723lib_evas_libevas_la_CPPFLAGS += \
1724-I$(top_srcdir)/src/static_libs/lz4 \
1725-I$(top_srcdir)/src/static_libs/rg_etc \
1726@evas_image_loader_tgv_cflags@
1727lib_evas_libevas_la_LIBADD += @evas_image_loader_tgv_libs@
1728if EVAS_CSERVE2
1729bin_evas_evas_cserve2_slave_SOURCES += \
1730modules/evas/loaders/tgv/evas_image_load_tgv.c \
1731static_libs/rg_etc/rg_etc1.c \
1732static_libs/rg_etc/rg_etc1.h \
1733static_libs/lz4/lz4.c \
1734static_libs/lz4/lz4.h
1735bin_evas_evas_cserve2_slave_CPPFLAGS += \
1736-I$(top_builddir)/src/lib/efl \
1737-I$(top_srcdir)/src/static_libs/lz4 \
1738-I$(top_srcdir)/src/static_libs/rg_etc \
1739-I$(top_srcdir)/src/lib/evas/ \
1740@evas_image_loader_tgv_cflags@
1741bin_evas_evas_cserve2_slave_LDADD += @evas_image_loader_tgv_libs@
1742endif
1743else
1744loadertgvpkgdir = $(libdir)/evas/modules/loaders/tgv/$(MODULE_ARCH)
1745loadertgvpkg_LTLIBRARIES = modules/evas/loaders/tgv/module.la
1746modules_evas_loaders_tgv_module_la_SOURCES = \
1747modules/evas/loaders/tgv/evas_image_load_tgv.c \
1748static_libs/rg_etc/rg_etc1.c \
1749static_libs/rg_etc/rg_etc1.h \
1750static_libs/lz4/lz4.c \
1751static_libs/lz4/lz4.h
1752modules_evas_loaders_tgv_module_la_CPPFLAGS = \
1753-I$(top_builddir)/src/lib/efl \
1754-I$(top_srcdir)/src/static_libs/lz4 \
1755-I$(top_srcdir)/src/static_libs/rg_etc \
1756-I$(top_srcdir)/src/lib/evas/ \
1757@EVAS_CFLAGS@ \
1758@evas_image_loader_tgv_cflags@
1759modules_evas_loaders_tgv_module_la_LIBADD = \
1760@USE_EVAS_LIBS@ \
1761@evas_image_loader_tgv_libs@
1762modules_evas_loaders_tgv_module_la_DEPENDENCIES = @USE_EVAS_INTERNAL_LIBS@
1763modules_evas_loaders_tgv_module_la_LDFLAGS = -module @EFL_LTMODULE_FLAGS@
1764modules_evas_loaders_tgv_module_la_LIBTOOLFLAGS = --tag=disable-static
1765endif
1766endif
1715 1767
1716### Unit tests 1768### Unit tests
1717 1769
diff --git a/src/lib/evas/common/evas_image_load.c b/src/lib/evas/common/evas_image_load.c
index 92c6402f8c..52d3295ec4 100644
--- a/src/lib/evas/common/evas_image_load.c
+++ b/src/lib/evas/common/evas_image_load.c
@@ -64,6 +64,9 @@ static const struct ext_loader_s loaders[] =
64 MATCHING(".cur", "ico"), 64 MATCHING(".cur", "ico"),
65 65
66 MATCHING(".psd", "psd"), 66 MATCHING(".psd", "psd"),
67
68 MATCHING(".tgv", "tgv"),
69
67 /* xcf - gefenric */ 70 /* xcf - gefenric */
68 MATCHING(".xcf", "generic"), 71 MATCHING(".xcf", "generic"),
69 MATCHING(".xcf.gz", "generic"), 72 MATCHING(".xcf.gz", "generic"),
diff --git a/src/lib/evas/file/evas_module.c b/src/lib/evas/file/evas_module.c
index 28b4243cdd..6cb5dc66bd 100644
--- a/src/lib/evas/file/evas_module.c
+++ b/src/lib/evas/file/evas_module.c
@@ -140,6 +140,7 @@ EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tiff);
140EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, wbmp); 140EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, wbmp);
141EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, webp); 141EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, webp);
142EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, xpm); 142EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, xpm);
143EVAS_EINA_STATIC_MODULE_DEFINE(image_loader, tgv);
143#endif 144#endif
144 145
145#if !EVAS_MODULE_NO_IMAGE_SAVERS 146#if !EVAS_MODULE_NO_IMAGE_SAVERS
@@ -232,6 +233,9 @@ static const struct {
232#ifdef EVAS_STATIC_BUILD_XPM 233#ifdef EVAS_STATIC_BUILD_XPM
233 EVAS_EINA_STATIC_MODULE_USE(image_loader, xpm), 234 EVAS_EINA_STATIC_MODULE_USE(image_loader, xpm),
234#endif 235#endif
236#ifdef EVAS_STATIC_BUILD_TGV
237 EVAS_EINA_STATIC_MODULE_USE(image_loader, tgv),
238#endif
235#endif 239#endif
236#if !EVAS_MODULE_NO_IMAGE_SAVERS 240#if !EVAS_MODULE_NO_IMAGE_SAVERS
237#ifdef EVAS_STATIC_BUILD_EET 241#ifdef EVAS_STATIC_BUILD_EET
diff --git a/src/modules/evas/loaders/tgv/evas_image_load_tgv.c b/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
new file mode 100644
index 0000000000..308d0b6f22
--- /dev/null
+++ b/src/modules/evas/loaders/tgv/evas_image_load_tgv.c
@@ -0,0 +1,359 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#ifdef HAVE_NETINET_IN_H
6# include <netinet/in.h>
7#endif
8
9#ifdef _WIN32
10# include <winsock2.h>
11#endif /* ifdef _WIN32 */
12
13#include "lz4.h"
14#include "rg_etc1.h"
15#include "Evas_Loader.h"
16
17/**************************************************************
18 * The TGV file format is oriented around compression mecanism
19 * that hardware are good at decompressing. We do still provide
20 * a fully software implementation in case your hardware doesn't
21 * handle it.
22 *
23 * This file format is designed to compress/decompress things
24 * in block area. Giving opportunity to store really huge file
25 * and only decompress/compress them as we need.
26 *
27 * The file format is as follow :
28 * - char magic[4]: "TGV1"
29 * - uint8_t block_size (real block size = (4 << bits[0-3], 4 << bits[4-7])
30 * - uint8_t algorithm (0 -> ETC1)
31 * - uint8_t options[2] (1 -> lz4)
32 * - uint32_t width
33 * - uint32_t height
34 * - blocks[]
35 * - 0 length encoded compress size (if length == 64 * block_size => no compression)
36 * - lzma encoded etc1 block
37 **************************************************************/
38
39// FIXME: wondering if we should support mipmap
40// FIXME: instead of the complete size, maybe just the usefull left over byte + number of block.
41
42typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
43struct _Evas_Loader_Internal
44{
45 Eina_File *f;
46
47 Eina_Rectangle region;
48
49 struct {
50 unsigned int width;
51 unsigned int height;
52 } block;
53 struct {
54 unsigned int width;
55 unsigned int height;
56 } size;
57
58 Eina_Bool compress;
59};
60
61
62static void *
63evas_image_load_file_open_tgv(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
64 Evas_Image_Load_Opts *opts,
65 Evas_Image_Animated *animated EINA_UNUSED,
66 int *error)
67{
68 Evas_Loader_Internal *loader;
69
70 loader = calloc(1, sizeof (Evas_Loader_Internal));
71 if (!loader)
72 {
73 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
74 return NULL;
75 }
76
77 if (eina_file_size_get(f) <= 16)
78 {
79 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
80 return NULL;
81 }
82
83 loader->f = eina_file_dup(f);
84 if (!loader->f)
85 {
86 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
87 return NULL;
88 }
89
90 if (opts && (opts->region.w > 0) && (opts->region.h > 0))
91 {
92 EINA_RECTANGLE_SET(&loader->region,
93 opts->region.x,
94 opts->region.y,
95 opts->region.w,
96 opts->region.h);
97 }
98 else
99 {
100 EINA_RECTANGLE_SET(&loader->region,
101 0, 0,
102 -1, -1);
103 }
104
105 return loader;
106}
107
108
109static void
110evas_image_load_file_close_tgv(void *loader_data)
111{
112 Evas_Loader_Internal *loader = loader_data;
113
114 eina_file_close(loader->f);
115 free(loader);
116}
117
118#define OFFSET_BLOCK_SIZE 4
119#define OFFSET_ALGORITHN 5
120#define OFFSET_OPTIONS 6
121#define OFFSET_WIDTH 8
122#define OFFSET_HEIGHT 12
123#define OFFSET_BLOCKS 16
124
125static Eina_Bool
126evas_image_load_file_head_tgv(void *loader_data,
127 Evas_Image_Property *prop,
128 int *error)
129{
130 Evas_Loader_Internal *loader = loader_data;
131 const char *m;
132
133 m = eina_file_map_all(loader->f, EINA_FILE_SEQUENTIAL);
134 if (!m)
135 {
136 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
137 return EINA_FALSE;
138 }
139
140 if (strncmp(m, "TGV1", 4) != 0)
141 {
142 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
143 return EINA_FALSE;
144 }
145
146 loader->block.width = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f);
147 loader->block.height = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4);
148
149 if (m[OFFSET_ALGORITHN] != 0)
150 {
151 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
152 return EINA_FALSE;
153 }
154
155 loader->compress = m[OFFSET_OPTIONS] & 0x1;
156
157 loader->size.width = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH])));
158 loader->size.height = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT])));
159
160 if (loader->region.w == -1 &&
161 loader->region.h == -1)
162 {
163 loader->region.w = loader->size.width;
164 loader->region.h = loader->size.height;
165 }
166 else
167 {
168 Eina_Rectangle r;
169
170 EINA_RECTANGLE_SET(&r, 0, 0, loader->size.width, loader->size.height);
171 if (!eina_rectangle_intersection(&loader->region, &r))
172 {
173 *error = EVAS_LOAD_ERROR_GENERIC;
174 return EINA_FALSE;
175 }
176 }
177
178 prop->w = loader->size.width;
179 prop->h = loader->size.height;
180
181 return EINA_TRUE;
182}
183
184static inline unsigned int
185_tgv_length_get(const char *m, unsigned int length, unsigned int *offset)
186{
187 unsigned int r = 0;
188 unsigned int shift = 0;
189
190 while (*offset < length && ((*m) & 0x80))
191 {
192 r = r | (((*m) & 0x7F) << shift);
193 shift += 7;
194 m++;
195 (*offset)++;
196 }
197 if (*offset < length)
198 {
199 r = r | (((*m) & 0x7F) << shift);
200 (*offset)++;
201 }
202
203 return r;
204}
205
206Eina_Bool
207evas_image_load_file_data_tgv(void *loader_data,
208 Evas_Image_Property *prop,
209 void *pixels,
210 int *error)
211{
212 Evas_Loader_Internal *loader = loader_data;
213 const char *m;
214 unsigned int *p = pixels;
215 char *buffer;
216 Eina_Rectangle master;
217 unsigned int block_length;
218 unsigned int length, offset;
219 unsigned int x, y;
220 unsigned int block_count;
221 Eina_Bool r = EINA_FALSE;
222
223 length = eina_file_size_get(loader->f);
224 offset = OFFSET_BLOCKS;
225
226 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
227
228 m = eina_file_map_all(loader->f, EINA_FILE_WILLNEED);
229 if (!m) return EINA_FALSE;
230
231 // By definition, prop{.w, .h} == region{.w, .h}
232 EINA_RECTANGLE_SET(&master,
233 loader->region.x, loader->region.y,
234 prop->w, prop->h);
235
236 // Allocate space for each ETC1 block (64bytes per 4 * 4 pixels group)
237 block_count = loader->block.width * loader->block.height / (4 * 4);
238 if (loader->compress)
239 buffer = alloca(8 * block_count);
240 else
241 buffer = NULL;
242
243 for (y = 0; y < loader->size.height; y += loader->block.height)
244 for (x = 0; x < loader->size.width; x += loader->block.width)
245 {
246 Eina_Rectangle current;
247 const char *data_start;
248 const char *it;
249 unsigned int expand_length;
250 unsigned int i, j;
251
252 block_length = _tgv_length_get(m + offset, length, &offset);
253
254 if (block_length == 0) goto on_error;
255
256 data_start = m + offset;
257 offset += block_length;
258
259 EINA_RECTANGLE_SET(&current, x, y,
260 loader->block.width, loader->block.height);
261
262 if (!eina_rectangle_intersection(&current, &master))
263 continue ;
264
265 if (loader->compress)
266 {
267 expand_length = LZ4_uncompress(data_start,
268 buffer, block_count * 8);
269 // That's an overhead for now, need to be fixed
270 if (expand_length != block_length)
271 goto on_error;
272 }
273 else
274 {
275 buffer = (void*) data_start;
276 if (block_count * 8 != block_length)
277 goto on_error;
278 }
279 it = buffer;
280
281 for (i = 0; i < loader->block.height; i += 4)
282 for (j = 0; j < loader->block.width; j += 4, it += 8)
283 {
284 Eina_Rectangle current_etc;
285 unsigned int temporary[4 * 4] = { 0 };
286 unsigned int offset_x, offset_y;
287 int k;
288
289 EINA_RECTANGLE_SET(&current_etc, x + j, y + i, 4, 4);
290
291 if (!eina_rectangle_intersection(&current_etc, &current))
292 continue ;
293
294 if (!rg_etc1_unpack_block(it, temporary, 0))
295 {
296 fprintf(stderr, "HOUSTON WE HAVE A PROBLEM ! Block starting at {%i, %i} is corrupted !\n", x + j, y + i);
297 continue ;
298 }
299
300 offset_x = current_etc.x - x - j;
301 offset_y = current_etc.y - y - i;
302 for (k = 0; k < current_etc.h; k++)
303 {
304 memcpy(&p[current_etc.x +
305 (current_etc.y + k) * loader->region.w],
306 &temporary[offset_x + (offset_y + k) * 4],
307 current_etc.w * sizeof (unsigned int));
308 }
309 }
310 }
311
312 r = EINA_TRUE;
313 *error = EVAS_LOAD_ERROR_NONE;
314
315 on_error:
316 eina_file_map_free(loader->f, (void*) m);
317 return r;
318}
319
320Evas_Image_Load_Func evas_image_load_tgv_func =
321{
322 evas_image_load_file_open_tgv,
323 evas_image_load_file_close_tgv,
324 evas_image_load_file_head_tgv,
325 evas_image_load_file_data_tgv,
326 NULL,
327 EINA_TRUE,
328 EINA_FALSE
329};
330
331static int
332module_open(Evas_Module *em)
333{
334 if (!em) return 0;
335 em->functions = (void *)(&evas_image_load_tgv_func);
336 return 1;
337}
338
339static void
340module_close(Evas_Module *em EINA_UNUSED)
341{
342}
343
344static Evas_Module_Api evas_modapi =
345{
346 EVAS_MODULE_API_VERSION,
347 "tgv",
348 "none",
349 {
350 module_open,
351 module_close
352 }
353};
354
355EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, tgv);
356
357#ifndef EVAS_STATIC_BUILD_TGV
358EVAS_EINA_MODULE_DEFINE(image_loader, tgv);
359#endif