downstream: cosmetic changes (tabulation etc)
[profile/ivi/weston-ivi-shell.git] / shared / image-loader.c
1 /*
2  * Copyright © 2008-2012 Kristian Høgsberg
3  * Copyright © 2012 Intel Corporation
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting documentation, and
9  * that the name of the copyright holders not be used in advertising or
10  * publicity pertaining to distribution of the software without specific,
11  * written prior permission.  The copyright holders make no representations
12  * about the suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21  * OF THIS SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <jpeglib.h>
31 #include <png.h>
32 #include <pixman.h>
33
34 #include "image-loader.h"
35
36 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
37
38 #ifdef HAVE_WEBP
39 #include <webp/decode.h>
40 #endif
41
42 static int
43 stride_for_width(int width)
44 {
45         return width * 4;
46 }
47
48 static void
49 swizzle_row(JSAMPLE *row, JDIMENSION width)
50 {
51         JSAMPLE *s;
52         uint32_t *d;
53
54         s = row + (width - 1) * 3;
55         d = (uint32_t *) (row + (width - 1) * 4);
56         while (s >= row) {
57                 *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0);
58                 s -= 3;
59                 d--;
60         }
61 }
62
63 static void
64 error_exit(j_common_ptr cinfo)
65 {
66         longjmp(cinfo->client_data, 1);
67 }
68
69 static void
70 pixman_image_destroy_func(pixman_image_t *image, void *data)
71 {
72         free(data);
73 }
74
75 static pixman_image_t *
76 load_jpeg(FILE *fp)
77 {
78         struct jpeg_decompress_struct cinfo;
79         struct jpeg_error_mgr jerr;
80         pixman_image_t *pixman_image = NULL;
81         unsigned int i;
82         int stride, first;
83         JSAMPLE *data, *rows[4];
84         jmp_buf env;
85
86         cinfo.err = jpeg_std_error(&jerr);
87         jerr.error_exit = error_exit;
88         cinfo.client_data = env;
89         if (setjmp(env))
90                 return NULL;
91
92         jpeg_create_decompress(&cinfo);
93
94         jpeg_stdio_src(&cinfo, fp);
95
96         jpeg_read_header(&cinfo, TRUE);
97
98         cinfo.out_color_space = JCS_RGB;
99         jpeg_start_decompress(&cinfo);
100
101         stride = cinfo.output_width * 4;
102         data = malloc(stride * cinfo.output_height);
103         if (data == NULL) {
104                 fprintf(stderr, "couldn't allocate image data\n");
105                 return NULL;
106         }
107
108         while (cinfo.output_scanline < cinfo.output_height) {
109                 first = cinfo.output_scanline;
110                 for (i = 0; i < ARRAY_LENGTH(rows); i++)
111                         rows[i] = data + (first + i) * stride;
112
113                 jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
114                 for (i = 0; first + i < cinfo.output_scanline; i++)
115                         swizzle_row(rows[i], cinfo.output_width);
116         }
117
118         jpeg_finish_decompress(&cinfo);
119
120         jpeg_destroy_decompress(&cinfo);
121
122         pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
123                                         cinfo.output_width,
124                                         cinfo.output_height,
125                                         (uint32_t *) data, stride);
126
127         pixman_image_set_destroy_function(pixman_image,
128                                 pixman_image_destroy_func, data);
129
130         return pixman_image;
131 }
132
133 static inline int
134 multiply_alpha(int alpha, int color)
135 {
136     int temp = (alpha * color) + 0x80;
137
138     return ((temp + (temp >> 8)) >> 8);
139 }
140
141 static void
142 premultiply_data(png_structp   png,
143                  png_row_infop row_info,
144                  png_bytep     data)
145 {
146     unsigned int i;
147     png_bytep p;
148
149     for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
150         png_byte  alpha = p[3];
151         uint32_t w;
152
153         if (alpha == 0) {
154                 w = 0;
155         } else {
156                 png_byte red   = p[0];
157                 png_byte green = p[1];
158                 png_byte blue  = p[2];
159
160                 if (alpha != 0xff) {
161                         red   = multiply_alpha(alpha, red);
162                         green = multiply_alpha(alpha, green);
163                         blue  = multiply_alpha(alpha, blue);
164                 }
165                 w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
166         }
167
168         * (uint32_t *) p = w;
169     }
170 }
171
172 static void
173 read_func(png_structp png, png_bytep data, png_size_t size)
174 {
175         FILE *fp = png_get_io_ptr(png);
176
177         if (fread(data, 1, size, fp) != size)
178                 png_error(png, NULL);
179 }
180
181 static void
182 png_error_callback(png_structp png, png_const_charp error_msg)
183 {
184     longjmp (png_jmpbuf (png), 1);
185 }
186
187 static pixman_image_t *
188 load_png(FILE *fp)
189 {
190         png_struct *png;
191         png_info *info;
192         png_byte *data = NULL;
193         png_byte **row_pointers = NULL;
194         png_uint_32 width, height;
195         int depth, color_type, interlace, stride;
196         unsigned int i;
197         pixman_image_t *pixman_image = NULL;
198
199         png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
200                                      png_error_callback, NULL);
201         if (!png)
202                 return NULL;
203
204         info = png_create_info_struct(png);
205         if (!info) {
206                 png_destroy_read_struct(&png, &info, NULL);
207                 return NULL;
208         }
209
210         if (setjmp(png_jmpbuf(png))) {
211                 if (data)
212                         free(data);
213                 if (row_pointers)
214                         free(row_pointers);
215                 png_destroy_read_struct(&png, &info, NULL);
216                 return NULL;
217         }
218
219         png_set_read_fn(png, fp, read_func);
220         png_read_info(png, info);
221         png_get_IHDR(png, info,
222                      &width, &height, &depth,
223                      &color_type, &interlace, NULL, NULL);
224
225         if (color_type == PNG_COLOR_TYPE_PALETTE)
226                 png_set_palette_to_rgb(png);
227
228         if (color_type == PNG_COLOR_TYPE_GRAY)
229                 png_set_expand_gray_1_2_4_to_8(png);
230
231         if (png_get_valid(png, info, PNG_INFO_tRNS))
232                 png_set_tRNS_to_alpha(png);
233
234         if (depth == 16)
235                 png_set_strip_16(png);
236
237         if (depth < 8)
238                 png_set_packing(png);
239
240         if (color_type == PNG_COLOR_TYPE_GRAY ||
241             color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
242                 png_set_gray_to_rgb(png);
243
244         if (interlace != PNG_INTERLACE_NONE)
245                 png_set_interlace_handling(png);
246
247         png_set_filler(png, 0xff, PNG_FILLER_AFTER);
248         png_set_read_user_transform_fn(png, premultiply_data);
249         png_read_update_info(png, info);
250         png_get_IHDR(png, info,
251                      &width, &height, &depth,
252                      &color_type, &interlace, NULL, NULL);
253
254
255         stride = stride_for_width(width);
256         data = malloc(stride * height);
257         if (!data) {
258                 png_destroy_read_struct(&png, &info, NULL);
259                 return NULL;
260         }
261
262         row_pointers = malloc(height * sizeof row_pointers[0]);
263         if (row_pointers == NULL) {
264                 free(data);
265                 png_destroy_read_struct(&png, &info, NULL);
266                 return NULL;
267         }
268
269         for (i = 0; i < height; i++)
270                 row_pointers[i] = &data[i * stride];
271
272         png_read_image(png, row_pointers);
273         png_read_end(png, info);
274
275         free(row_pointers);
276         png_destroy_read_struct(&png, &info, NULL);
277
278         pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
279                                 width, height, (uint32_t *) data, stride);
280
281         pixman_image_set_destroy_function(pixman_image,
282                                 pixman_image_destroy_func, data);
283
284         return pixman_image;
285 }
286
287 #ifdef HAVE_WEBP
288
289 static pixman_image_t *
290 load_webp(FILE *fp)
291 {
292         WebPDecoderConfig config;
293         uint8_t buffer[16 * 1024];
294         int len;
295         VP8StatusCode status;
296         WebPIDecoder *idec;
297
298         if (!WebPInitDecoderConfig(&config)) {
299                 fprintf(stderr, "Library version mismatch!\n");
300                 return NULL;
301         }
302
303         /* webp decoding api doesn't seem to specify a min size that's
304            usable for GetFeatures, but 256 works... */
305         len = fread(buffer, 1, 256, fp);
306         status = WebPGetFeatures(buffer, len, &config.input);
307         if (status != VP8_STATUS_OK) {
308                 fprintf(stderr, "failed to parse webp header\n");
309                 WebPFreeDecBuffer(&config.output);
310                 return NULL;
311         }
312
313         config.output.colorspace = MODE_BGRA;
314         config.output.u.RGBA.stride = stride_for_width(config.input.width);
315         config.output.u.RGBA.size =
316                 config.output.u.RGBA.stride * config.input.height;
317         config.output.u.RGBA.rgba =
318                 malloc(config.output.u.RGBA.stride * config.input.height);
319         config.output.is_external_memory = 1;
320         if (!config.output.u.RGBA.rgba) {
321                 WebPFreeDecBuffer(&config.output);
322                 return NULL;
323         }
324
325         rewind(fp);
326         idec = WebPINewDecoder(&config.output);
327         if (!idec) {
328                 WebPFreeDecBuffer(&config.output);
329                 return NULL;
330         }
331
332         while (!feof(fp)) {
333                 len = fread(buffer, 1, sizeof buffer, fp);
334                 status = WebPIAppend(idec, buffer, len);
335                 if (status != VP8_STATUS_OK) {
336                         fprintf(stderr, "webp decode status %d\n", status);
337                         WebPIDelete(idec);
338                         WebPFreeDecBuffer(&config.output);
339                         return NULL;
340                 }
341         }
342
343         WebPIDelete(idec);
344         WebPFreeDecBuffer(&config.output);
345
346         return pixman_image_create_bits(PIXMAN_a8r8g8b8,
347                                         config.input.width,
348                                         config.input.height,
349                                         (uint32_t *) config.output.u.RGBA.rgba,
350                                         config.output.u.RGBA.stride);
351 }
352
353 #endif
354
355
356 struct image_loader {
357         unsigned char header[4];
358         int header_size;
359         pixman_image_t *(*load)(FILE *fp);
360 };
361
362 static const struct image_loader loaders[] = {
363         { { 0x89, 'P', 'N', 'G' }, 4, load_png },
364         { { 0xff, 0xd8 }, 2, load_jpeg },
365 #ifdef HAVE_WEBP
366         { { 'R', 'I', 'F', 'F' }, 4, load_webp }
367 #endif
368 };
369
370 pixman_image_t *
371 load_image(const char *filename)
372 {
373         pixman_image_t *image;
374         unsigned char header[4];
375         FILE *fp;
376         unsigned int i;
377
378         if (!filename || !*filename)
379                 return NULL;
380
381         fp = fopen(filename, "rb");
382         if (!fp) {
383                 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
384                 return NULL;
385         }
386
387         if (fread(header, sizeof header, 1, fp) != 1) {
388                 fclose(fp);
389                 fprintf(stderr, "%s: unable to read file header\n", filename);
390                 return NULL;
391         }
392
393         rewind(fp);
394         for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
395                 if (memcmp(header, loaders[i].header,
396                            loaders[i].header_size) == 0) {
397                         image = loaders[i].load(fp);
398                         break;
399                 }
400         }
401
402         fclose(fp);
403
404         if (i == ARRAY_LENGTH(loaders)) {
405                 fprintf(stderr, "%s: unrecognized file header "
406                         "0x%02x 0x%02x 0x%02x 0x%02x\n",
407                         filename, header[0], header[1], header[2], header[3]);
408                 image = NULL;
409         } else if (!image) {
410                 /* load probably printed something, but just in case */
411                 fprintf(stderr, "%s: error reading image\n", filename);
412         }
413
414         return image;
415 }