21b20a23c4e0aa4e6e3029c681c56699f8bb2d70
[profile/ivi/weston.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 <stdlib.h>
27 #include <stdio.h>
28 #include <jpeglib.h>
29 #include <png.h>
30 #include <pixman.h>
31
32 #include "config-parser.h"
33
34 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
35
36 #ifdef HAVE_WEBP
37 #include <webp/decode.h>
38 #endif
39
40 static int
41 stride_for_width(int width)
42 {
43         return width * 4;
44 }
45
46 static void
47 swizzle_row(JSAMPLE *row, JDIMENSION width)
48 {
49         JSAMPLE *s;
50         uint32_t *d;
51
52         s = row + (width - 1) * 3;
53         d = (uint32_t *) (row + (width - 1) * 4);
54         while (s >= row) {
55                 *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0);
56                 s -= 3;
57                 d--;
58         }
59 }
60
61 static void
62 error_exit(j_common_ptr cinfo)
63 {
64         longjmp(cinfo->client_data, 1);
65 }
66
67 static pixman_image_t *
68 load_jpeg(FILE *fp)
69 {
70         struct jpeg_decompress_struct cinfo;
71         struct jpeg_error_mgr jerr;
72         unsigned int i;
73         int stride, first;
74         JSAMPLE *data, *rows[4];
75         jmp_buf env;
76
77         cinfo.err = jpeg_std_error(&jerr);
78         jerr.error_exit = error_exit;
79         cinfo.client_data = env;
80         if (setjmp(env))
81                 return NULL;
82
83         jpeg_create_decompress(&cinfo);
84
85         jpeg_stdio_src(&cinfo, fp);
86
87         jpeg_read_header(&cinfo, TRUE);
88
89         cinfo.out_color_space = JCS_RGB;
90         jpeg_start_decompress(&cinfo);
91
92         stride = cinfo.output_width * 4;
93         data = malloc(stride * cinfo.output_height);
94         if (data == NULL) {
95                 fprintf(stderr, "couldn't allocate image data\n");
96                 return NULL;
97         }
98
99         while (cinfo.output_scanline < cinfo.output_height) {
100                 first = cinfo.output_scanline;
101                 for (i = 0; i < ARRAY_LENGTH(rows); i++)
102                         rows[i] = data + (first + i) * stride;
103
104                 jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
105                 for (i = 0; first + i < cinfo.output_scanline; i++)
106                         swizzle_row(rows[i], cinfo.output_width);
107         }
108
109         jpeg_finish_decompress(&cinfo);
110
111         jpeg_destroy_decompress(&cinfo);
112
113         return pixman_image_create_bits(PIXMAN_a8r8g8b8,
114                                         cinfo.output_width,
115                                         cinfo.output_height,
116                                         (uint32_t *) data, stride);
117 }
118
119 static inline int
120 multiply_alpha(int alpha, int color)
121 {
122     int temp = (alpha * color) + 0x80;
123
124     return ((temp + (temp >> 8)) >> 8);
125 }
126
127 static void
128 premultiply_data(png_structp   png,
129                  png_row_infop row_info,
130                  png_bytep     data)
131 {
132     unsigned int i;
133     png_bytep p;
134
135     for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
136         png_byte  alpha = p[3];
137         uint32_t w;
138
139         if (alpha == 0) {
140                 w = 0;
141         } else {
142                 png_byte red   = p[0];
143                 png_byte green = p[1];
144                 png_byte blue  = p[2];
145
146                 if (alpha != 0xff) {
147                         red   = multiply_alpha(alpha, red);
148                         green = multiply_alpha(alpha, green);
149                         blue  = multiply_alpha(alpha, blue);
150                 }
151                 w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
152         }
153
154         * (uint32_t *) p = w;
155     }
156 }
157
158 static void
159 read_func(png_structp png, png_bytep data, png_size_t size)
160 {
161         FILE *fp = png_get_io_ptr(png);
162
163         if (fread(data, 1, size, fp) != size)
164                 png_error(png, NULL);
165 }
166
167 static void
168 png_error_callback(png_structp png, png_const_charp error_msg)
169 {
170     longjmp (png_jmpbuf (png), 1);
171 }
172
173 static pixman_image_t *
174 load_png(FILE *fp)
175 {
176         png_struct *png;
177         png_info *info;
178         png_byte *data = NULL;
179         png_byte **row_pointers = NULL;
180         png_uint_32 width, height;
181         int depth, color_type, interlace, stride;
182         unsigned int i;
183
184         png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
185                                      png_error_callback, NULL);
186         if (!png)
187                 return NULL;
188
189         info = png_create_info_struct(png);
190         if (!info) {
191                 png_destroy_read_struct(&png, &info, NULL);
192                 return NULL;
193         }
194
195         if (setjmp(png_jmpbuf(png))) {
196                 if (data)
197                         free(data);
198                 if (row_pointers)
199                         free(row_pointers);
200                 png_destroy_read_struct(&png, &info, NULL);
201                 return NULL;
202         }
203
204         png_set_read_fn(png, fp, read_func);
205         png_read_info(png, info);
206         png_get_IHDR(png, info,
207                      &width, &height, &depth,
208                      &color_type, &interlace, NULL, NULL);
209
210         if (color_type == PNG_COLOR_TYPE_PALETTE)
211                 png_set_palette_to_rgb(png);
212
213         if (color_type == PNG_COLOR_TYPE_GRAY)
214                 png_set_expand_gray_1_2_4_to_8(png);
215
216         if (png_get_valid(png, info, PNG_INFO_tRNS))
217                 png_set_tRNS_to_alpha(png);
218
219         if (depth == 16)
220                 png_set_strip_16(png);
221
222         if (depth < 8)
223                 png_set_packing(png);
224
225         if (color_type == PNG_COLOR_TYPE_GRAY ||
226             color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
227                 png_set_gray_to_rgb(png);
228
229         if (interlace != PNG_INTERLACE_NONE)
230                 png_set_interlace_handling(png);
231
232         png_set_filler(png, 0xff, PNG_FILLER_AFTER);
233         png_set_read_user_transform_fn(png, premultiply_data);
234         png_read_update_info(png, info);
235         png_get_IHDR(png, info,
236                      &width, &height, &depth,
237                      &color_type, &interlace, NULL, NULL);
238
239
240         stride = stride_for_width(width);
241         data = malloc(stride * height);
242         if (!data) {
243                 png_destroy_read_struct(&png, &info, NULL);
244                 return NULL;
245         }
246
247         row_pointers = malloc(height * sizeof row_pointers[0]);
248         if (row_pointers == NULL) {
249                 free(data);
250                 png_destroy_read_struct(&png, &info, NULL);
251                 return NULL;
252         }
253
254         for (i = 0; i < height; i++)
255                 row_pointers[i] = &data[i * stride];
256
257         png_read_image(png, row_pointers);
258         png_read_end(png, info);
259
260         free(row_pointers);
261         png_destroy_read_struct(&png, &info, NULL);
262
263         return pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height,
264                                         (uint32_t *) data, stride);
265 }
266
267 #ifdef HAVE_WEBP
268
269 static pixman_image_t *
270 load_webp(FILE *fp)
271 {
272         WebPDecoderConfig config;
273         uint8_t buffer[16 * 1024];
274         int len;
275         VP8StatusCode status;
276         WebPIDecoder *idec;
277
278         if (!WebPInitDecoderConfig(&config)) {
279                 fprintf(stderr, "Library version mismatch!\n");
280                 return NULL;
281         }
282
283         /* webp decoding api doesn't seem to specify a min size that's
284            usable for GetFeatures, but 256 works... */
285         len = fread(buffer, 1, 256, fp);
286         status = WebPGetFeatures(buffer, len, &config.input);
287         if (status != VP8_STATUS_OK) {
288                 fprintf(stderr, "failed to parse webp header\n");
289                 WebPFreeDecBuffer(&config.output);
290                 return NULL;
291         }
292
293         config.output.colorspace = MODE_BGRA;
294         config.output.u.RGBA.stride = stride_for_width(config.input.width);
295         config.output.u.RGBA.size =
296                 config.output.u.RGBA.stride * config.input.height;
297         config.output.u.RGBA.rgba =
298                 malloc(config.output.u.RGBA.stride * config.input.height);
299         config.output.is_external_memory = 1;
300         if (!config.output.u.RGBA.rgba) {
301                 WebPFreeDecBuffer(&config.output);
302                 return NULL;
303         }
304
305         rewind(fp);
306         idec = WebPINewDecoder(&config.output);
307         if (!idec) {
308                 WebPFreeDecBuffer(&config.output);
309                 return NULL;
310         }
311
312         while (!feof(fp)) {
313                 len = fread(buffer, 1, sizeof buffer, fp);
314                 status = WebPIAppend(idec, buffer, len);
315                 if (status != VP8_STATUS_OK) {
316                         fprintf(stderr, "webp decode status %d\n", status);
317                         WebPIDelete(idec);
318                         WebPFreeDecBuffer(&config.output);
319                         return NULL;
320                 }
321         }
322
323         WebPIDelete(idec);
324         WebPFreeDecBuffer(&config.output);
325
326         return pixman_image_create_bits(PIXMAN_a8r8g8b8,
327                                         config.input.width,
328                                         config.input.height,
329                                         (uint32_t *) config.output.u.RGBA.rgba,
330                                         config.output.u.RGBA.stride);
331 }
332
333 #endif
334
335
336 struct image_loader {
337         char header[4];
338         int header_size;
339         pixman_image_t *(*load)(FILE *fp);
340 };
341
342 static const struct image_loader loaders[] = {
343         { { 0x89, 'P', 'N', 'G' }, 4, load_png },
344         { { 0xff, 0xd8 }, 2, load_jpeg },
345 #ifdef HAVE_WEBP
346         { { 'R', 'I', 'F', 'F' }, 4, load_webp }
347 #endif
348 };
349
350 pixman_image_t *
351 load_image(const char *filename)
352 {
353         pixman_image_t *image;
354         unsigned char header[4];
355         FILE *fp;
356         unsigned int i;
357
358         fp = fopen(filename, "rb");
359         if (fp == NULL)
360                 return NULL;
361
362         if (fread(header, sizeof header, 1, fp) != 1)
363                 return NULL;
364
365         rewind(fp);
366         for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
367                 if (memcmp(header, loaders[i].header,
368                            loaders[i].header_size) == 0) {
369                         image = loaders[i].load(fp);
370                         break;
371                 }
372         }
373
374         fclose(fp);
375
376         if (i == ARRAY_LENGTH(loaders)) {
377                 fprintf(stderr, "unrecognized file header for %s: "
378                         "0x%02x 0x%02x 0x%02x 0x%02x\n",
379                         filename, header[0], header[1], header[2], header[3]);
380                 image = NULL;
381         }
382
383         return image;
384 }