blob: b01266c877cf2eb84bdbc34d102894b2ea8ca4ea [file] [log] [blame]
Kristian Høgsbergf02a6492012-03-12 01:05:25 -04001/*
2 * Copyright © 2008-2012 Kristian Høgsberg
3 * Copyright © 2012 Intel Corporation
4 *
Bryce Harrington6c6164c2015-06-11 14:20:17 -07005 * 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:
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040012 *
Bryce Harrington6c6164c2015-06-11 14:20:17 -070013 * 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.
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040025 */
26
Daniel Stonec228e232013-05-22 18:03:19 +030027#include "config.h"
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040028
Bill Spitzak28371f72014-08-19 18:13:10 -070029#include <errno.h>
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040030#include <stdlib.h>
31#include <stdio.h>
Armin Keb3c73f2013-04-03 21:29:03 +020032#include <string.h>
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040033#include <jpeglib.h>
34#include <png.h>
35#include <pixman.h>
36
Kristian Høgsberg3c2360f2013-01-28 16:01:22 -050037#include "image-loader.h"
Kristian Høgsbergee4b4cb2012-04-11 09:43:53 -040038
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040039#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
40
41#ifdef HAVE_WEBP
42#include <webp/decode.h>
43#endif
44
45static int
46stride_for_width(int width)
47{
48 return width * 4;
49}
50
51static void
52swizzle_row(JSAMPLE *row, JDIMENSION width)
53{
54 JSAMPLE *s;
55 uint32_t *d;
56
57 s = row + (width - 1) * 3;
58 d = (uint32_t *) (row + (width - 1) * 4);
59 while (s >= row) {
60 *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0);
61 s -= 3;
62 d--;
63 }
64}
65
66static void
67error_exit(j_common_ptr cinfo)
68{
69 longjmp(cinfo->client_data, 1);
70}
71
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +020072static void
73pixman_image_destroy_func(pixman_image_t *image, void *data)
74{
75 free(data);
76}
77
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040078static pixman_image_t *
79load_jpeg(FILE *fp)
80{
81 struct jpeg_decompress_struct cinfo;
82 struct jpeg_error_mgr jerr;
Kristian Høgsbergc234fb82012-07-11 15:45:59 -040083 pixman_image_t *pixman_image = NULL;
Kristian Høgsbergee4b4cb2012-04-11 09:43:53 -040084 unsigned int i;
85 int stride, first;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040086 JSAMPLE *data, *rows[4];
87 jmp_buf env;
88
89 cinfo.err = jpeg_std_error(&jerr);
90 jerr.error_exit = error_exit;
91 cinfo.client_data = env;
92 if (setjmp(env))
93 return NULL;
94
95 jpeg_create_decompress(&cinfo);
96
97 jpeg_stdio_src(&cinfo, fp);
98
99 jpeg_read_header(&cinfo, TRUE);
100
101 cinfo.out_color_space = JCS_RGB;
102 jpeg_start_decompress(&cinfo);
103
104 stride = cinfo.output_width * 4;
105 data = malloc(stride * cinfo.output_height);
106 if (data == NULL) {
107 fprintf(stderr, "couldn't allocate image data\n");
108 return NULL;
109 }
110
111 while (cinfo.output_scanline < cinfo.output_height) {
112 first = cinfo.output_scanline;
113 for (i = 0; i < ARRAY_LENGTH(rows); i++)
114 rows[i] = data + (first + i) * stride;
115
116 jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
117 for (i = 0; first + i < cinfo.output_scanline; i++)
118 swizzle_row(rows[i], cinfo.output_width);
119 }
120
121 jpeg_finish_decompress(&cinfo);
122
123 jpeg_destroy_decompress(&cinfo);
124
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200125 pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400126 cinfo.output_width,
127 cinfo.output_height,
128 (uint32_t *) data, stride);
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200129
130 pixman_image_set_destroy_function(pixman_image,
131 pixman_image_destroy_func, data);
132
133 return pixman_image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400134}
135
136static inline int
137multiply_alpha(int alpha, int color)
138{
139 int temp = (alpha * color) + 0x80;
140
141 return ((temp + (temp >> 8)) >> 8);
142}
143
144static void
145premultiply_data(png_structp png,
146 png_row_infop row_info,
147 png_bytep data)
148{
149 unsigned int i;
150 png_bytep p;
151
152 for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
153 png_byte alpha = p[3];
154 uint32_t w;
155
156 if (alpha == 0) {
157 w = 0;
158 } else {
159 png_byte red = p[0];
160 png_byte green = p[1];
161 png_byte blue = p[2];
162
163 if (alpha != 0xff) {
164 red = multiply_alpha(alpha, red);
165 green = multiply_alpha(alpha, green);
166 blue = multiply_alpha(alpha, blue);
167 }
168 w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
169 }
170
171 * (uint32_t *) p = w;
172 }
173}
174
175static void
176read_func(png_structp png, png_bytep data, png_size_t size)
177{
178 FILE *fp = png_get_io_ptr(png);
179
Jonas Ådahl3685c3a2012-03-30 23:10:27 +0200180 if (fread(data, 1, size, fp) != size)
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400181 png_error(png, NULL);
182}
183
184static void
185png_error_callback(png_structp png, png_const_charp error_msg)
186{
187 longjmp (png_jmpbuf (png), 1);
188}
189
190static pixman_image_t *
191load_png(FILE *fp)
192{
193 png_struct *png;
194 png_info *info;
195 png_byte *data = NULL;
196 png_byte **row_pointers = NULL;
197 png_uint_32 width, height;
198 int depth, color_type, interlace, stride;
199 unsigned int i;
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200200 pixman_image_t *pixman_image = NULL;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400201
202 png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
203 png_error_callback, NULL);
204 if (!png)
205 return NULL;
206
207 info = png_create_info_struct(png);
208 if (!info) {
209 png_destroy_read_struct(&png, &info, NULL);
210 return NULL;
211 }
212
213 if (setjmp(png_jmpbuf(png))) {
214 if (data)
215 free(data);
216 if (row_pointers)
217 free(row_pointers);
218 png_destroy_read_struct(&png, &info, NULL);
219 return NULL;
220 }
221
222 png_set_read_fn(png, fp, read_func);
223 png_read_info(png, info);
224 png_get_IHDR(png, info,
225 &width, &height, &depth,
226 &color_type, &interlace, NULL, NULL);
227
228 if (color_type == PNG_COLOR_TYPE_PALETTE)
229 png_set_palette_to_rgb(png);
230
231 if (color_type == PNG_COLOR_TYPE_GRAY)
232 png_set_expand_gray_1_2_4_to_8(png);
233
234 if (png_get_valid(png, info, PNG_INFO_tRNS))
235 png_set_tRNS_to_alpha(png);
236
237 if (depth == 16)
238 png_set_strip_16(png);
239
240 if (depth < 8)
241 png_set_packing(png);
242
243 if (color_type == PNG_COLOR_TYPE_GRAY ||
244 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
245 png_set_gray_to_rgb(png);
246
247 if (interlace != PNG_INTERLACE_NONE)
248 png_set_interlace_handling(png);
249
250 png_set_filler(png, 0xff, PNG_FILLER_AFTER);
251 png_set_read_user_transform_fn(png, premultiply_data);
252 png_read_update_info(png, info);
253 png_get_IHDR(png, info,
254 &width, &height, &depth,
255 &color_type, &interlace, NULL, NULL);
256
257
258 stride = stride_for_width(width);
259 data = malloc(stride * height);
260 if (!data) {
261 png_destroy_read_struct(&png, &info, NULL);
262 return NULL;
263 }
264
265 row_pointers = malloc(height * sizeof row_pointers[0]);
266 if (row_pointers == NULL) {
267 free(data);
268 png_destroy_read_struct(&png, &info, NULL);
269 return NULL;
270 }
271
272 for (i = 0; i < height; i++)
273 row_pointers[i] = &data[i * stride];
274
275 png_read_image(png, row_pointers);
276 png_read_end(png, info);
277
278 free(row_pointers);
279 png_destroy_read_struct(&png, &info, NULL);
280
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200281 pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
282 width, height, (uint32_t *) data, stride);
283
284 pixman_image_set_destroy_function(pixman_image,
285 pixman_image_destroy_func, data);
286
287 return pixman_image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400288}
289
290#ifdef HAVE_WEBP
291
292static pixman_image_t *
293load_webp(FILE *fp)
294{
295 WebPDecoderConfig config;
296 uint8_t buffer[16 * 1024];
297 int len;
298 VP8StatusCode status;
299 WebPIDecoder *idec;
300
301 if (!WebPInitDecoderConfig(&config)) {
302 fprintf(stderr, "Library version mismatch!\n");
303 return NULL;
304 }
305
306 /* webp decoding api doesn't seem to specify a min size that's
307 usable for GetFeatures, but 256 works... */
308 len = fread(buffer, 1, 256, fp);
309 status = WebPGetFeatures(buffer, len, &config.input);
310 if (status != VP8_STATUS_OK) {
311 fprintf(stderr, "failed to parse webp header\n");
312 WebPFreeDecBuffer(&config.output);
313 return NULL;
314 }
315
316 config.output.colorspace = MODE_BGRA;
317 config.output.u.RGBA.stride = stride_for_width(config.input.width);
318 config.output.u.RGBA.size =
319 config.output.u.RGBA.stride * config.input.height;
320 config.output.u.RGBA.rgba =
321 malloc(config.output.u.RGBA.stride * config.input.height);
322 config.output.is_external_memory = 1;
323 if (!config.output.u.RGBA.rgba) {
324 WebPFreeDecBuffer(&config.output);
325 return NULL;
326 }
327
328 rewind(fp);
329 idec = WebPINewDecoder(&config.output);
330 if (!idec) {
331 WebPFreeDecBuffer(&config.output);
332 return NULL;
333 }
334
335 while (!feof(fp)) {
336 len = fread(buffer, 1, sizeof buffer, fp);
337 status = WebPIAppend(idec, buffer, len);
338 if (status != VP8_STATUS_OK) {
339 fprintf(stderr, "webp decode status %d\n", status);
340 WebPIDelete(idec);
341 WebPFreeDecBuffer(&config.output);
342 return NULL;
343 }
344 }
345
346 WebPIDelete(idec);
347 WebPFreeDecBuffer(&config.output);
348
349 return pixman_image_create_bits(PIXMAN_a8r8g8b8,
350 config.input.width,
351 config.input.height,
352 (uint32_t *) config.output.u.RGBA.rgba,
353 config.output.u.RGBA.stride);
354}
355
356#endif
357
358
359struct image_loader {
Kristian Høgsbergb71302e2012-05-10 12:28:35 -0400360 unsigned char header[4];
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400361 int header_size;
362 pixman_image_t *(*load)(FILE *fp);
363};
364
365static const struct image_loader loaders[] = {
366 { { 0x89, 'P', 'N', 'G' }, 4, load_png },
367 { { 0xff, 0xd8 }, 2, load_jpeg },
368#ifdef HAVE_WEBP
369 { { 'R', 'I', 'F', 'F' }, 4, load_webp }
370#endif
371};
372
373pixman_image_t *
374load_image(const char *filename)
375{
376 pixman_image_t *image;
377 unsigned char header[4];
378 FILE *fp;
Kristian Høgsbergee4b4cb2012-04-11 09:43:53 -0400379 unsigned int i;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400380
Bill Spitzak28371f72014-08-19 18:13:10 -0700381 if (!filename || !*filename)
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400382 return NULL;
383
Bill Spitzak28371f72014-08-19 18:13:10 -0700384 fp = fopen(filename, "rb");
385 if (!fp) {
386 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
387 return NULL;
388 }
389
Martin Olsson712f5f42012-07-08 03:03:41 +0200390 if (fread(header, sizeof header, 1, fp) != 1) {
391 fclose(fp);
Bill Spitzak28371f72014-08-19 18:13:10 -0700392 fprintf(stderr, "%s: unable to read file header\n", filename);
Jonas Ådahl3685c3a2012-03-30 23:10:27 +0200393 return NULL;
Martin Olsson712f5f42012-07-08 03:03:41 +0200394 }
Jonas Ådahl3685c3a2012-03-30 23:10:27 +0200395
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400396 rewind(fp);
397 for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
398 if (memcmp(header, loaders[i].header,
399 loaders[i].header_size) == 0) {
400 image = loaders[i].load(fp);
401 break;
402 }
403 }
404
405 fclose(fp);
406
407 if (i == ARRAY_LENGTH(loaders)) {
Bill Spitzak28371f72014-08-19 18:13:10 -0700408 fprintf(stderr, "%s: unrecognized file header "
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400409 "0x%02x 0x%02x 0x%02x 0x%02x\n",
410 filename, header[0], header[1], header[2], header[3]);
411 image = NULL;
Bill Spitzak28371f72014-08-19 18:13:10 -0700412 } else if (!image) {
413 /* load probably printed something, but just in case */
414 fprintf(stderr, "%s: error reading image\n", filename);
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400415 }
416
417 return image;
418}