summaryrefslogtreecommitdiff
path: root/src/lib/emile
diff options
context:
space:
mode:
authorCedric BAIL <cedric@osg.samsung.com>2015-03-17 08:50:15 +0100
committerCedric BAIL <cedric@osg.samsung.com>2015-03-17 09:58:18 +0100
commitd40dad8f73bb367b7735212f22b4594725069fa0 (patch)
treed7e38bfaa797985b86795cbbe49716ff5c8d4c51 /src/lib/emile
parent65248db8d8868e9e6b95f0544b78767a03542376 (diff)
emile: initial addition of emile image support.
Diffstat (limited to 'src/lib/emile')
-rw-r--r--src/lib/emile/Emile.h158
-rw-r--r--src/lib/emile/emile_image.c801
2 files changed, 959 insertions, 0 deletions
diff --git a/src/lib/emile/Emile.h b/src/lib/emile/Emile.h
index 47a9186d3b..ff3ce7e1cf 100644
--- a/src/lib/emile/Emile.h
+++ b/src/lib/emile/Emile.h
@@ -174,6 +174,164 @@ EAPI Eina_Bool emile_binbuf_expand(const Eina_Binbuf *in,
174 Eina_Binbuf *out, 174 Eina_Binbuf *out,
175 Emile_Compressor_Type t); 175 Emile_Compressor_Type t);
176 176
177/* All the value from below enum should be the same as in Evas_Loader.h */
178typedef enum _Emile_Colorspace
179{
180 EMILE_COLORSPACE_ARGB8888,/**< ARGB 32 bits per pixel, high-byte is Alpha, accessed 1 32bit word at a time */
181 EMILE_COLORSPACE_YCBCR422P601_PL, /**< YCbCr 4:2:2 Planar, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb, then Cr rows */
182 EMILE_COLORSPACE_YCBCR422P709_PL, /**< YCbCr 4:2:2 Planar, ITU.BT-709 specifications. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb, then Cr rows */
183 EMILE_COLORSPACE_RGB565_A5P, /**< 16bit rgb565 + Alpha plane at end - 5 bits of the 8 being used per alpha byte */
184 EMILE_COLORSPACE_GRY8 = 4,
185 EMILE_COLORSPACE_YCBCR422601_PL, /**< YCbCr 4:2:2, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to line of Y,Cb,Y,Cr bytes */
186 EMILE_COLORSPACE_YCBCR420NV12601_PL, /**< YCbCr 4:2:0, ITU.BT-601 specification. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb,Cr rows. */
187 EMILE_COLORSPACE_YCBCR420TM12601_PL, /**< YCbCr 4:2:0, ITU.BT-601 specification. The data pointed to is just an array of tiled row pointer, pointing to the Y rows, then the Cb,Cr rows. */
188 EMILE_COLORSPACE_AGRY88 = 8, /**< AY 8bits Alpha and 8bits Grey, accessed 1 16bits at a time */
189 EMILE_COLORSPACE_ETC1 = 9, /**< OpenGL ETC1 encoding of RGB texture (4 bit per pixel) @since 1.10 */
190 EMILE_COLORSPACE_RGB8_ETC2 = 10, /**< OpenGL GL_COMPRESSED_RGB8_ETC2 texture compression format (4 bit per pixel) @since 1.10 */
191 EMILE_COLORSPACE_RGBA8_ETC2_EAC = 11, /**< OpenGL GL_COMPRESSED_RGBA8_ETC2_EAC texture compression format, supports alpha (8 bit per pixel) @since 1.10 */
192 EMILE_COLORSPACE_ETC1_ALPHA = 12, /**< ETC1 with alpha support using two planes: ETC1 RGB and ETC1 grey for alpha @since 1.11 */
193 EMILE_COLORSPACE_RGB_S3TC_DXT1 = 13, /**< OpenGL COMPRESSED_RGB_S3TC_DXT1_EXT format with RGB only. @since 1.11 */
194 EMILE_COLORSPACE_RGBA_S3TC_DXT1 = 14, /**< OpenGL COMPRESSED_RGBA_S3TC_DXT1_EXT format with RGBA punchthrough. @since 1.11 */
195 EMILE_COLORSPACE_RGBA_S3TC_DXT2 = 15, /**< DirectDraw DXT2 format with premultiplied RGBA. Not supported by OpenGL itself. @since 1.11 */
196 EMILE_COLORSPACE_RGBA_S3TC_DXT3 = 16, /**< OpenGL COMPRESSED_RGBA_S3TC_DXT3_EXT format with RGBA. @since 1.11 */
197 EMILE_COLORSPACE_RGBA_S3TC_DXT4 = 17, /**< DirectDraw DXT4 format with premultiplied RGBA. Not supported by OpenGL itself. @since 1.11 */
198 EMILE_COLORSPACE_RGBA_S3TC_DXT5 = 18 /**< OpenGL COMPRESSED_RGBA_S3TC_DXT5_EXT format with RGBA. @since 1.11 */
199} Emile_Colorspace;
200
201typedef enum _Emile_Image_Encoding
202{
203 EMILE_IMAGE_LOSSLESS = 0,
204 EMILE_IMAGE_JPEG = 1,
205 EMILE_IMAGE_ETC1 = 2,
206 EMILE_IMAGE_ETC2_RGB = 3,
207 EMILE_IMAGE_ETC2_RGBA = 4,
208 EMILE_IMAGE_ETC1_ALPHA = 5
209} Emile_Image_Encoding;
210
211typedef enum _Emile_Image_Scale_Hint
212{
213 EMILE_IMAGE_SCALE_HINT_NONE = 0, /**< No scale hint at all */
214 EMILE_IMAGE_SCALE_HINT_DYNAMIC = 1, /**< Image is being re-scaled over time, thus turning scaling cache @b off for its data */
215 EMILE_IMAGE_SCALE_HINT_STATIC = 2 /**< Image is not being re-scaled over time, thus turning scaling cache @b on for its data */
216} Emile_Image_Scale_Hint;
217
218typedef enum _Emile_Image_Animated_Loop_Hint
219{
220 EMILE_IMAGE_ANIMATED_HINT_NONE = 0,
221 EMILE_IMAGE_ANIMATED_HINT_LOOP = 1,
222 EMILE_IMAGE_ANIMATED_HINT_PINGPONG = 2
223} Emile_Image_Animated_Loop_Hint;
224
225typedef enum _Emile_Image_Load_Error
226{
227 EMILE_IMAGE_LOAD_ERROR_NONE = 0, /**< No error on load */
228 EMILE_IMAGE_LOAD_ERROR_GENERIC = 1, /**< A non-specific error occurred */
229 EMILE_IMAGE_LOAD_ERROR_DOES_NOT_EXIST = 2, /**< File (or file path) does not exist */
230 EMILE_IMAGE_LOAD_ERROR_PERMISSION_DENIED = 3, /**< Permission denied to an existing file (or path) */
231 EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED = 4, /**< Allocation of resources failure prevented load */
232 EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE = 5, /**< File corrupt (but was detected as a known format) */
233 EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT = 6 /**< File is not a known format */
234} Emile_Image_Load_Error; /**< Emile image load error codes one can get - see emile_load_error_str() too. */
235
236typedef struct _Emile_Image Emile_Image;
237typedef struct _Emile_Image_Property Emile_Image_Property;
238typedef struct _Emile_Image_Load_Opts Emile_Image_Load_Opts;
239typedef struct _Emile_Image_Animated Emile_Image_Animated;
240
241struct _Emile_Image_Property
242{
243 struct {
244 unsigned char l, r, t, b;
245 } borders;
246
247 const Emile_Colorspace *cspaces;
248 Emile_Colorspace cspace;
249
250 Emile_Image_Encoding encoding;
251
252 unsigned int w;
253 unsigned int h;
254 unsigned int row_stride;
255
256 unsigned char scale;
257
258 Eina_Bool rotated;
259 Eina_Bool alpha;
260 Eina_Bool premul;
261 Eina_Bool alpha_sparse;
262
263 Eina_Bool flipped;
264 Eina_Bool comp;
265};
266
267struct _Emile_Image_Animated
268{
269 Eina_List *frames;
270
271 Emile_Image_Animated_Loop_Hint loop_hint;
272
273 int frame_count;
274 int loop_count;
275 int cur_frame;
276
277 Eina_Bool animated;
278};
279
280struct _Emile_Image_Load_Opts
281{
282 struct {
283 int x, y, w, h;
284 } region;
285 struct {
286 int src_x, src_y, src_w, src_h;
287 int dst_w, dst_h;
288 int smooth;
289 Emile_Image_Scale_Hint scale_hint;
290 } scale_load;
291 double dpi;
292 unsigned int w, h;
293 unsigned int degree;
294 int scale_down_by;
295
296 Eina_Bool orientation;
297};
298
299// FIXME: Add enum for error code
300// FIXME: should set region at load time, not head time
301
302EAPI Emile_Image *emile_image_tgv_memory_open(Eina_Binbuf *source,
303 Emile_Image_Load_Opts *opts,
304 Emile_Image_Animated *animated,
305 Emile_Image_Load_Error *error);
306EAPI Emile_Image *emile_image_tgv_file_open(Eina_File *source,
307 Emile_Image_Load_Opts *opts,
308 Emile_Image_Animated *animated,
309 Emile_Image_Load_Error *error);
310
311EAPI Emile_Image *emile_image_jpeg_memory_open(Eina_Binbuf *source,
312 Emile_Image_Load_Opts *opts,
313 Emile_Image_Animated *animated,
314 Emile_Image_Load_Error *error);
315EAPI Emile_Image *emile_image_jpeg_file_open(Eina_File *source,
316 Emile_Image_Load_Opts *opts,
317 Emile_Image_Animated *animated,
318 Emile_Image_Load_Error *error);
319
320EAPI Eina_Bool emile_image_head(Emile_Image *image,
321 Emile_Image_Property *prop,
322 unsigned int property_size,
323 Emile_Image_Load_Error *error);
324EAPI Eina_Bool emile_image_data(Emile_Image *image,
325 Emile_Image_Property *prop,
326 unsigned int property_size,
327 void *pixels,
328 Emile_Image_Load_Error *error);
329
330EAPI void emile_image_close(Emile_Image *source);
331
332EAPI const char *emile_load_error_str(Emile_Image *source,
333 Emile_Image_Load_Error error);
334
177#ifdef __cplusplus 335#ifdef __cplusplus
178} 336}
179#endif /* ifdef __cplusplus */ 337#endif /* ifdef __cplusplus */
diff --git a/src/lib/emile/emile_image.c b/src/lib/emile/emile_image.c
new file mode 100644
index 0000000000..9f26a9aa00
--- /dev/null
+++ b/src/lib/emile/emile_image.c
@@ -0,0 +1,801 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif /* ifdef HAVE_CONFIG_H */
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#ifdef ENABLE_LIBLZ4
14# include <lz4.h>
15#else
16# include "lz4.h"
17#endif
18
19#include "rg_etc1.h"
20#include "Emile.h"
21
22#ifdef BUILD_NEON
23#include <arm_neon.h>
24#endif
25
26#ifndef WORDS_BIGENDIAN
27/* x86 */
28#define A_VAL(p) (((uint8_t *)(p))[3])
29#define R_VAL(p) (((uint8_t *)(p))[2])
30#define G_VAL(p) (((uint8_t *)(p))[1])
31#define B_VAL(p) (((uint8_t *)(p))[0])
32#else
33/* ppc */
34#define A_VAL(p) (((uint8_t *)(p))[0])
35#define R_VAL(p) (((uint8_t *)(p))[1])
36#define G_VAL(p) (((uint8_t *)(p))[2])
37#define B_VAL(p) (((uint8_t *)(p))[3])
38#endif
39
40#define OFFSET_BLOCK_SIZE 4
41#define OFFSET_ALGORITHM 5
42#define OFFSET_OPTIONS 6
43#define OFFSET_WIDTH 8
44#define OFFSET_HEIGHT 12
45#define OFFSET_BLOCKS 16
46
47struct _Emile_Image
48{
49 Emile_Image_Load_Opts opts;
50
51 struct {
52 unsigned int width;
53 unsigned int height;
54 } size, block;
55
56 Eina_Rectangle region;
57
58 union {
59 Eina_Binbuf *bin;
60 Eina_File *f;
61 } source;
62
63 const unsigned char *source_data;
64
65 Eina_Bool (*bind)(Emile_Image *image,
66 Emile_Image_Load_Opts *opts,
67 Emile_Image_Animated *animated,
68 Emile_Image_Load_Error *error);
69 Eina_Bool (*head)(Emile_Image *image,
70 Emile_Image_Property *prop,
71 unsigned int property_size,
72 Emile_Image_Load_Error *error);
73 Eina_Bool (*data)(Emile_Image *image,
74 Emile_Image_Property *prop,
75 unsigned int property_size,
76 void *pixels,
77 Emile_Image_Load_Error *error);
78 void (*close)(Emile_Image *image);
79
80 Emile_Colorspace cspace;
81
82 Eina_Bool bin_source : 1;
83
84 /* TGV option */
85 Eina_Bool unpremul : 1;
86 Eina_Bool compress : 1;
87 Eina_Bool blockless : 1;
88 Eina_Bool load_opts : 1;
89};
90
91static const unsigned char *
92_emile_image_file_source_map(Emile_Image *image, unsigned int *length)
93{
94 if (!image) return NULL;
95
96 if (image->bin_source)
97 {
98 if (length) *length = eina_binbuf_length_get(image->source.bin);
99 return eina_binbuf_string_get(image->source.bin);
100 }
101
102 if (length) *length = eina_file_size_get(image->source.f);
103 if (!image->source_data)
104 {
105 image->source_data = eina_file_map_all(image->source.f,
106 EINA_FILE_SEQUENTIAL);
107 }
108 return image->source_data;
109}
110
111static void
112_emile_image_file_source_unmap(Emile_Image *image)
113{
114 if (!(image && image->source_data)) return ;
115 eina_file_map_free(image->source.f,
116 (void*) image->source_data);
117 image->source_data = NULL;
118}
119
120static int
121roundup(int val, int rup)
122{
123 if (val >= 0 && rup > 0)
124 return (val + rup - 1) - ((val + rup - 1) % rup);
125 return 0;
126}
127
128/* TGV Handling */
129
130static const Emile_Colorspace cspaces_etc1[2] = {
131 EMILE_COLORSPACE_ETC1,
132 EMILE_COLORSPACE_ARGB8888
133};
134
135static const Emile_Colorspace cspaces_rgb8_etc2[2] = {
136 EMILE_COLORSPACE_RGB8_ETC2,
137 EMILE_COLORSPACE_ARGB8888
138};
139
140static const Emile_Colorspace cspaces_rgba8_etc2_eac[2] = {
141 EMILE_COLORSPACE_RGBA8_ETC2_EAC,
142 EMILE_COLORSPACE_ARGB8888
143};
144
145static const Emile_Colorspace cspaces_etc1_alpha[2] = {
146 EMILE_COLORSPACE_ETC1_ALPHA,
147 EMILE_COLORSPACE_ARGB8888
148};
149
150/**************************************************************
151 * The TGV file format is oriented around compression mecanism
152 * that hardware are good at decompressing. We do still provide
153 * a fully software implementation in case your hardware doesn't
154 * handle it. As OpenGL is pretty bad at handling border of
155 * texture, we do duplicate the first pixels of every border.
156 *
157 * This file format is designed to compress/decompress things
158 * in block area. Giving opportunity to store really huge file
159 * and only decompress/compress them as we need. Note that region
160 * only work with software decompression as we don't have a sane
161 * way to duplicate border to avoid artifact when scaling texture.
162 *
163 * The file format is as follow :
164 * - char magic[4]: "TGV1"
165 * - uint8_t block_size (real block size = (4 << bits[0-3], 4 << bits[4-7])
166 * - uint8_t algorithm (0 -> ETC1, 1 -> ETC2 RGB, 2 -> ETC2 RGBA, 3 -> ETC1+Alpha)
167 * - uint8_t options[2] (bitmask: 1 -> lz4, 2 for block-less, 4 -> unpremultiplied)
168 * - uint32_t width
169 * - uint32_t height
170 * - blocks[]
171 * - 0 length encoded compress size (if length == 64 * block_size => no compression)
172 * - lzma encoded etc1 block
173 *
174 * If the format is ETC1+Alpha (algo = 3), then a second image is encoded
175 * in ETC1 right after the first one, and it contains grey-scale alpha
176 * values.
177 **************************************************************/
178
179// FIXME: wondering if we should support mipmap
180// TODO: support ETC1+ETC2 images (RGB only)
181
182static Eina_Bool
183_emile_tgv_bind(Emile_Image *image,
184 Emile_Image_Load_Opts *opts,
185 Emile_Image_Animated *animated,
186 Emile_Image_Load_Error *error)
187{
188 const unsigned char *m;
189 unsigned int length;
190
191 m = _emile_image_file_source_map(image, &length);
192 if (!m) return EINA_FALSE;
193
194 /* Fast check for file characteristic (useful when trying to guess
195 a file format without extention). */
196 if (length < 16 ||
197 strncmp((const char*) m, "TGV1", 4) != 0)
198 goto on_error;
199
200 if ((m[OFFSET_ALGORITHM] & 0xFF) > 3)
201 goto on_error;
202
203 return EINA_TRUE;
204
205 on_error:
206 *error = EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT;
207 _emile_image_file_source_unmap(image);
208 return EINA_FALSE;
209}
210
211static Eina_Bool
212_emile_tgv_head(Emile_Image *image,
213 Emile_Image_Property *prop,
214 unsigned int property_size,
215 Emile_Image_Load_Error *error)
216{
217 const unsigned char *m;
218 unsigned int length;
219
220 m = _emile_image_file_source_map(image, &length);
221 if (!m) return EINA_FALSE;
222
223 switch (m[OFFSET_ALGORITHM] & 0xFF)
224 {
225 case 0:
226 prop->cspaces = cspaces_etc1;
227 prop->alpha = EINA_FALSE;
228 image->cspace = EMILE_COLORSPACE_ETC1;
229 break;
230 case 1:
231 prop->cspaces = cspaces_rgb8_etc2;
232 prop->alpha = EINA_FALSE;
233 image->cspace = EMILE_COLORSPACE_RGB8_ETC2;
234 break;
235 case 2:
236 prop->cspaces = cspaces_rgba8_etc2_eac;
237 prop->alpha = EINA_TRUE;
238 image->cspace = EMILE_COLORSPACE_RGBA8_ETC2_EAC;
239 break;
240 case 3:
241 prop->cspaces = cspaces_etc1_alpha;
242 prop->alpha = EINA_TRUE;
243 prop->premul = !!(m[OFFSET_OPTIONS] & 0x4);
244 image->unpremul = prop->premul;
245 break;
246 default:
247 *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE;
248 return EINA_FALSE;
249 }
250
251 image->compress = m[OFFSET_OPTIONS] & 0x1;
252 image->blockless = (m[OFFSET_OPTIONS] & 0x2) != 0;
253
254 image->size.width = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH])));
255 image->size.height = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT])));
256
257 if (image->blockless)
258 {
259 image->block.width = roundup(image->size.width + 2, 4);
260 image->block.height = roundup(image->size.height + 2, 4);
261 }
262 else
263 {
264 image->block.width = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f);
265 image->block.height = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4);
266 }
267
268 EINA_RECTANGLE_SET(&image->region, 0, 0,
269 image->size.width, image->size.height);
270 if (image->load_opts &&
271 (image->opts.region.w > 0 && image->opts.region.h > 0))
272 {
273 // ETC colorspace doesn't work with region for now
274 prop->cspaces = NULL;
275
276 if (!eina_rectangle_intersection(&image->region,
277 &image->opts.region))
278 {
279 *error = EMILE_IMAGE_LOAD_ERROR_GENERIC;
280 return EINA_FALSE;
281 }
282 }
283
284 prop->w = image->size.width;
285 prop->h = image->size.height;
286 prop->borders.l = 1;
287 prop->borders.t = 1;
288 prop->borders.r = roundup(prop->w + 2, 4) - prop->w - 1;
289 prop->borders.b = roundup(prop->h + 2, 4) - prop->h - 1;
290
291 return EINA_TRUE;
292}
293
294static inline unsigned int
295_tgv_length_get(const unsigned char *m,
296 unsigned int length,
297 unsigned int *offset)
298{
299 unsigned int r = 0;
300 unsigned int shift = 0;
301
302 while (*offset < length && ((*m) & 0x80))
303 {
304 r = r | (((*m) & 0x7F) << shift);
305 shift += 7;
306 m++;
307 (*offset)++;
308 }
309 if (*offset < length)
310 {
311 r = r | (((*m) & 0x7F) << shift);
312 (*offset)++;
313 }
314
315 return r;
316}
317
318static Eina_Bool
319_emile_tgv_data(Emile_Image *image,
320 Emile_Image_Property *prop,
321 unsigned int property_size,
322 void *pixels,
323 Emile_Image_Load_Error *error)
324{
325 const unsigned char *m;
326 unsigned int *p = pixels;
327 unsigned char *p_etc = pixels;
328 Eina_Binbuf *buffer = NULL;
329 Eina_Rectangle master;
330 unsigned int block_length;
331 unsigned int length, offset;
332 unsigned int x, y;
333 unsigned int block_count;
334 unsigned int etc_width = 0;
335 unsigned int etc_block_size;
336 int num_planes = 1, plane, alpha_offset = 0;
337 Eina_Bool r = EINA_FALSE;
338
339 m = _emile_image_file_source_map(image, &length);
340 if (!m)
341 {
342 *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
343 return EINA_FALSE;
344 }
345
346 offset = OFFSET_BLOCKS;
347
348 *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE;
349
350 // By definition, prop{.w, .h} == region{.w, .h}
351 EINA_RECTANGLE_SET(&master,
352 image->region.x, image->region.y,
353 prop->w, prop->h);
354
355 switch (image->cspace)
356 {
357 case EMILE_COLORSPACE_ETC1:
358 case EMILE_COLORSPACE_RGB8_ETC2:
359 etc_block_size = 8;
360 break;
361 case EMILE_COLORSPACE_RGBA8_ETC2_EAC:
362 etc_block_size = 16;
363 break;
364 case EMILE_COLORSPACE_ETC1_ALPHA:
365 etc_block_size = 8;
366 num_planes = 2;
367 alpha_offset = ((prop->w + 2 + 3) / 4) * ((prop->h + 2 + 3) / 4) * 8 / sizeof(*p_etc);
368 break;
369 default: abort();
370 }
371 etc_width = ((prop->w + 2 + 3) / 4) * etc_block_size;
372
373 switch (prop->cspace)
374 {
375 case EMILE_COLORSPACE_ETC1:
376 case EMILE_COLORSPACE_RGB8_ETC2:
377 case EMILE_COLORSPACE_RGBA8_ETC2_EAC:
378 case EMILE_COLORSPACE_ETC1_ALPHA:
379 if (master.x % 4 || master.y % 4)
380 // FIXME: Should we really abort here ? Seems like a late check for me
381 abort();
382 break;
383 case EMILE_COLORSPACE_ARGB8888:
384 // Offset to take duplicated pixels into account
385 master.x += 1;
386 master.y += 1;
387 break;
388 default: abort();
389 }
390
391
392 if (prop->cspace != EMILE_COLORSPACE_ARGB8888 && prop->cspace != image->cspace)
393 {
394 if (!((prop->cspace == EMILE_COLORSPACE_RGB8_ETC2) &&
395 (image->cspace == EMILE_COLORSPACE_ETC1)))
396 {
397 *error = EMILE_IMAGE_LOAD_ERROR_GENERIC;
398 return EINA_FALSE;
399 }
400 // else: ETC2 is compatible with ETC1 and is preferred
401 }
402
403 // Allocate space for each ETC block (8 or 16 bytes per 4 * 4 pixels group)
404 block_count = image->block.width * image->block.height / (4 * 4);
405 if (image->compress)
406 buffer = eina_binbuf_manage_read_only_new_length(alloca(etc_block_size * block_count), etc_block_size * block_count);
407
408 for (plane = 0; plane < num_planes; plane++)
409 for (y = 0; y < image->size.height + 2; y += image->block.height)
410 for (x = 0; x < image->size.width + 2; x += image->block.width)
411 {
412 Eina_Rectangle current;
413 Eina_Binbuf *data_start;
414 const unsigned char *it;
415 unsigned int expand_length;
416 unsigned int i, j;
417
418 block_length = _tgv_length_get(m + offset, length, &offset);
419
420 if (block_length == 0)
421 {
422 *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE;
423 return EINA_FALSE;
424 }
425
426 data_start = eina_binbuf_manage_read_only_new_length(m + offset, block_length);
427 offset += block_length;
428
429 EINA_RECTANGLE_SET(&current, x, y,
430 image->block.width, image->block.height);
431
432 if (!eina_rectangle_intersection(&current, &master))
433 continue;
434
435 if (image->compress)
436 {
437 if (!emile_binbuf_expand(data_start, buffer, EMILE_LZ4HC))
438 goto on_error;
439 }
440 else
441 {
442 buffer = data_start;
443 if (block_count * etc_block_size != block_length)
444 goto on_error;
445 }
446 it = eina_binbuf_string_get(buffer);
447
448 for (i = 0; i < image->block.height; i += 4)
449 for (j = 0; j < image->block.width; j += 4, it += etc_block_size)
450 {
451 Eina_Rectangle current_etc;
452 unsigned int temporary[4 * 4];
453 unsigned int offset_x, offset_y;
454 int k, l;
455
456 EINA_RECTANGLE_SET(&current_etc, x + j, y + i, 4, 4);
457
458 if (!eina_rectangle_intersection(&current_etc, &current))
459 continue;
460
461 switch (prop->cspace)
462 {
463 case EMILE_COLORSPACE_ARGB8888:
464 switch (image->cspace)
465 {
466 case EMILE_COLORSPACE_ETC1:
467 case EMILE_COLORSPACE_ETC1_ALPHA:
468 if (!rg_etc1_unpack_block(it, temporary, 0))
469 {
470 // TODO: Should we decode as RGB8_ETC2?
471 fprintf(stderr, "ETC1: Block starting at {%i, %i} is corrupted!\n", x + j, y + i);
472 continue;
473 }
474 break;
475 case EMILE_COLORSPACE_RGB8_ETC2:
476 rg_etc2_rgb8_decode_block((uint8_t *) it, temporary);
477 break;
478 case EMILE_COLORSPACE_RGBA8_ETC2_EAC:
479 rg_etc2_rgba8_decode_block((uint8_t *) it, temporary);
480 break;
481 default: abort();
482 }
483
484 offset_x = current_etc.x - x - j;
485 offset_y = current_etc.y - y - i;
486
487 if (!plane)
488 {
489#ifdef BUILD_NEON
490 if (eina_cpu_features_get() & EINA_CPU_NEON)
491 {
492 uint32_t *dst = &p[current_etc.x - 1 + (current_etc.y - 1) * master.w];
493 uint32_t *src = &temporary[offset_x + offset_y * 4];
494 for (k = 0; k < current_etc.h; k++)
495 {
496 if (current_etc.w == 4)
497 vst1q_u32(dst, vld1q_u32(src));
498 else if (current_etc.w == 3)
499 {
500 vst1_u32(dst, vld1_u32(src));
501 *(dst + 2) = *(src + 2);
502 }
503 else if (current_etc.w == 2)
504 vst1_u32(dst, vld1_u32(src));
505 else
506 *dst = *src;
507 dst += master.w;
508 src += 4;
509 }
510 }
511 else
512#endif
513 for (k = 0; k < current_etc.h; k++)
514 {
515 memcpy(&p[current_etc.x - 1 + (current_etc.y - 1 + k) * master.w],
516 &temporary[offset_x + (offset_y + k) * 4],
517 current_etc.w * sizeof (unsigned int));
518 }
519 }
520 else
521 {
522 for (k = 0; k < current_etc.h; k++)
523 for (l = 0; l < current_etc.w; l++)
524 {
525 unsigned int *rgbdata =
526 &p[current_etc.x - 1 + (current_etc.y - 1 + k) * master.w + l];
527 unsigned int *adata =
528 &temporary[offset_x + (offset_y + k) * 4 + l];
529 A_VAL(rgbdata) = G_VAL(adata);
530 }
531 }
532 break;
533 case EMILE_COLORSPACE_ETC1:
534 case EMILE_COLORSPACE_RGB8_ETC2:
535 case EMILE_COLORSPACE_RGBA8_ETC2_EAC:
536 memcpy(&p_etc[(current_etc.x / 4) * etc_block_size +
537 (current_etc.y / 4) * etc_width],
538 it, etc_block_size);
539 break;
540 case EMILE_COLORSPACE_ETC1_ALPHA:
541 memcpy(&p_etc[(current_etc.x / 4) * etc_block_size +
542 (current_etc.y / 4) * etc_width +
543 plane * alpha_offset],
544 it, etc_block_size);
545 break;
546 default:
547 abort();
548 }
549 } // bx,by inside blocks
550 } // x,y macroblocks
551
552 // TODO: Add support for more unpremultiplied modes (ETC2)
553 if (prop->cspace == EMILE_COLORSPACE_ARGB8888)
554 prop->premul = image->unpremul; // call premul if unpremul data
555
556 r = EINA_TRUE;
557
558 on_error:
559 return r;
560}
561
562static void
563_emile_tgv_close(Emile_Image *image EINA_UNUSED)
564{
565 // TGV file loader doesn't keep any data allocated around
566}
567
568/* JPEG Handling */
569static Eina_Bool
570_emile_jpeg_bind(Emile_Image *image,
571 Emile_Image_Load_Opts *opts,
572 Emile_Image_Animated *animated,
573 Emile_Image_Load_Error *error)
574{
575 return EINA_FALSE;
576}
577
578static Eina_Bool
579_emile_jpeg_head(Emile_Image *image,
580 Emile_Image_Property *prop,
581 unsigned int property_size,
582 Emile_Image_Load_Error *error)
583{
584 return EINA_FALSE;
585}
586
587static Eina_Bool
588_emile_jpeg_data(Emile_Image *image,
589 Emile_Image_Property *prop,
590 unsigned int property_size,
591 void *pixels,
592 Emile_Image_Load_Error *error)
593{
594 return EINA_FALSE;
595}
596
597static void
598_emile_jpeg_close(Emile_Image *image)
599{
600}
601
602/* Generic helper to instantiate a new Emile_Image */
603
604static Emile_Image *
605_emile_image_new(Eina_Bool (*bind) (Emile_Image *image,
606 Emile_Image_Load_Opts *opts,
607 Emile_Image_Animated *animated,
608 Emile_Image_Load_Error *error),
609 Eina_Bool (*head)(Emile_Image *image,
610 Emile_Image_Property *prop,
611 unsigned int property_size,
612 Emile_Image_Load_Error *error),
613 Eina_Bool (*data)(Emile_Image *image,
614 Emile_Image_Property *prop,
615 unsigned int property_size,
616 void *pixels,
617 Emile_Image_Load_Error *error),
618 void (*close)(Emile_Image *image))
619{
620 Emile_Image *ei;
621
622 ei = calloc(1, sizeof (Emile_Image));
623 if (!ei) return NULL;
624
625 ei->bind = bind;
626 ei->head = head;
627 ei->data = data;
628 ei->close = close;
629
630 return ei;
631}
632
633static void
634_emile_image_binbuf_set(Emile_Image *ei, Eina_Binbuf *source)
635{
636 ei->source.bin = source;
637 ei->bin_source = EINA_TRUE;
638}
639
640static void
641_emile_image_file_set(Emile_Image *ei, Eina_File *source)
642{
643 ei->source.f = eina_file_dup(source);
644 ei->bin_source = EINA_FALSE;
645}
646
647static Emile_Image *
648_emile_image_bind(Emile_Image *ei,
649 Emile_Image_Load_Opts *opts,
650 Emile_Image_Animated *animated,
651 Emile_Image_Load_Error *error)
652{
653 if (opts)
654 {
655 ei->opts = *opts;
656 ei->load_opts = 1;
657 }
658
659 *error = EMILE_IMAGE_LOAD_ERROR_NONE;
660 if (ei->bind(ei, opts, animated, error))
661 return ei;
662
663 /* File is not of that format */
664 free(ei);
665 return NULL;
666}
667
668/* Public API to manipulate Emile_Image */
669
670EAPI Emile_Image *
671emile_image_tgv_memory_open(Eina_Binbuf *source,
672 Emile_Image_Load_Opts *opts,
673 Emile_Image_Animated *animated,
674 Emile_Image_Load_Error *error)
675{
676 Emile_Image *ei;
677
678 ei = _emile_image_new(_emile_tgv_bind,
679 _emile_tgv_head,
680 _emile_tgv_data,
681 _emile_tgv_close);
682 if (!ei) return NULL;
683
684 _emile_image_binbuf_set(ei, source);
685 return _emile_image_bind(ei, opts, animated, error);
686}
687
688EAPI Emile_Image *
689emile_image_tgv_file_open(Eina_File *source,
690 Emile_Image_Load_Opts *opts,
691 Emile_Image_Animated *animated,
692 Emile_Image_Load_Error *error)
693{
694 Emile_Image *ei;
695
696 ei = _emile_image_new(_emile_tgv_bind,
697 _emile_tgv_head,
698 _emile_tgv_data,
699 _emile_tgv_close);
700 if (!ei) return NULL;
701
702 _emile_image_file_set(ei, source);
703 return _emile_image_bind(ei, opts, animated, error);
704}
705
706EAPI Emile_Image *
707emile_image_jpeg_memory_open(Eina_Binbuf *source,
708 Emile_Image_Load_Opts *opts,
709 Emile_Image_Animated *animated,
710 Emile_Image_Load_Error *error)
711{
712 Emile_Image *ei;
713
714 ei = _emile_image_new(_emile_jpeg_bind,
715 _emile_jpeg_head,
716 _emile_jpeg_data,
717 _emile_jpeg_close);
718 if (!ei) return NULL;
719
720 _emile_image_binbuf_set(ei, source);
721 return _emile_image_bind(ei, opts, animated, error);
722}
723
724EAPI Emile_Image *
725emile_image_jpeg_file_open(Eina_File *source,
726 Emile_Image_Load_Opts *opts,
727 Emile_Image_Animated *animated,
728 Emile_Image_Load_Error *error)
729{
730 Emile_Image *ei;
731
732 ei = _emile_image_new(_emile_jpeg_bind,
733 _emile_jpeg_head,
734 _emile_jpeg_data,
735 _emile_jpeg_close);
736 if (!ei) return NULL;
737
738 _emile_image_file_set(ei, source);
739 return _emile_image_bind(ei, opts, animated, error);
740}
741
742EAPI void
743emile_image_close(Emile_Image *image)
744{
745 if (!image) return ;
746
747 _emile_image_file_source_unmap(image);
748 if (!image->bin_source)
749 eina_file_close(image->source.f);
750 image->close(image);
751 free(image);
752}
753
754EAPI Eina_Bool
755emile_image_head(Emile_Image *image,
756 Emile_Image_Property *prop,
757 unsigned int property_size,
758 Emile_Image_Load_Error *error)
759{
760 if (!image) return EINA_FALSE;
761
762 *error = EMILE_IMAGE_LOAD_ERROR_NONE;
763 return image->head(image, prop, property_size, error);
764}
765
766EAPI Eina_Bool
767emile_image_data(Emile_Image *image,
768 Emile_Image_Property *prop,
769 unsigned int property_size,
770 void *pixels,
771 Emile_Image_Load_Error *error)
772{
773 if (!image) return EINA_FALSE;
774
775 *error = EMILE_IMAGE_LOAD_ERROR_NONE;
776 return image->data(image, prop, property_size, pixels, error);
777}
778
779EAPI const char *
780emile_load_error_str(Emile_Image *source EINA_UNUSED,
781 Emile_Image_Load_Error error)
782{
783 switch (error)
784 {
785 case EMILE_IMAGE_LOAD_ERROR_NONE:
786 return "No error";
787 case EMILE_IMAGE_LOAD_ERROR_GENERIC:
788 return "Generic error encountered while loading image.";
789 case EMILE_IMAGE_LOAD_ERROR_DOES_NOT_EXIST:
790 return "File does not exist.";
791 case EMILE_IMAGE_LOAD_ERROR_PERMISSION_DENIED:
792 return "Permission to open file denied.";
793 case EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED:
794 return "Not enough memory to open file.";
795 case EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE:
796 return "File is corrupted.";
797 case EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT:
798 return "Unexpected file format.";
799 }
800 return NULL;
801}