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