blob: 140968162a4bff77d433dfc64c8253b0c12f6a55 [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>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030031#include <stdint.h>
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040032#include <stdio.h>
Armin Keb3c73f2013-04-03 21:29:03 +020033#include <string.h>
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040034#include <png.h>
35#include <pixman.h>
36
Jon Cruz35b2eaa2015-06-15 15:37:08 -070037#include "shared/helpers.h"
Kristian Høgsberg3c2360f2013-01-28 16:01:22 -050038#include "image-loader.h"
Kristian Høgsbergee4b4cb2012-04-11 09:43:53 -040039
Emmanuel Gil Peyrot66e16142016-02-16 01:57:52 +000040#ifdef HAVE_JPEG
41#include <jpeglib.h>
42#endif
43
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040044#ifdef HAVE_WEBP
45#include <webp/decode.h>
46#endif
47
48static int
49stride_for_width(int width)
50{
51 return width * 4;
52}
53
54static void
Emmanuel Gil Peyrot66e16142016-02-16 01:57:52 +000055pixman_image_destroy_func(pixman_image_t *image, void *data)
56{
57 free(data);
58}
59
60#ifdef HAVE_JPEG
61
62static void
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040063swizzle_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
77static void
78error_exit(j_common_ptr cinfo)
79{
80 longjmp(cinfo->client_data, 1);
81}
82
83static pixman_image_t *
84load_jpeg(FILE *fp)
85{
86 struct jpeg_decompress_struct cinfo;
87 struct jpeg_error_mgr jerr;
Kristian Høgsbergc234fb82012-07-11 15:45:59 -040088 pixman_image_t *pixman_image = NULL;
Kristian Høgsbergee4b4cb2012-04-11 09:43:53 -040089 unsigned int i;
90 int stride, first;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040091 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
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200130 pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400131 cinfo.output_width,
132 cinfo.output_height,
133 (uint32_t *) data, stride);
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200134
135 pixman_image_set_destroy_function(pixman_image,
136 pixman_image_destroy_func, data);
137
138 return pixman_image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400139}
140
Emmanuel Gil Peyrot66e16142016-02-16 01:57:52 +0000141#else
142
143static pixman_image_t *
144load_jpeg(FILE *fp)
145{
146 fprintf(stderr, "JPEG support disabled at compile-time\n");
147 return NULL;
148}
149
150#endif
151
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400152static inline int
153multiply_alpha(int alpha, int color)
154{
155 int temp = (alpha * color) + 0x80;
156
157 return ((temp + (temp >> 8)) >> 8);
158}
159
160static void
161premultiply_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 png_byte alpha = p[3];
170 uint32_t w;
171
172 if (alpha == 0) {
173 w = 0;
174 } else {
175 png_byte red = p[0];
176 png_byte green = p[1];
177 png_byte 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
191static void
192read_func(png_structp png, png_bytep data, png_size_t size)
193{
194 FILE *fp = png_get_io_ptr(png);
195
Jonas Ådahl3685c3a2012-03-30 23:10:27 +0200196 if (fread(data, 1, size, fp) != size)
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400197 png_error(png, NULL);
198}
199
200static void
201png_error_callback(png_structp png, png_const_charp error_msg)
202{
203 longjmp (png_jmpbuf (png), 1);
204}
205
206static pixman_image_t *
207load_png(FILE *fp)
208{
209 png_struct *png;
210 png_info *info;
211 png_byte *data = NULL;
212 png_byte **row_pointers = NULL;
213 png_uint_32 width, height;
214 int depth, color_type, interlace, stride;
215 unsigned int i;
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200216 pixman_image_t *pixman_image = NULL;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400217
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
Rafal Mielniczuk9d4ddef2012-07-11 18:48:25 +0200297 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;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400304}
305
306#ifdef HAVE_WEBP
307
308static pixman_image_t *
309load_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
Emmanuel Gil Peyrot58b7a152016-02-16 01:57:51 +0000372#else
373
374static pixman_image_t *
375load_webp(FILE *fp)
376{
377 fprintf(stderr, "WebP support disabled at compile-time\n");
378 return NULL;
379}
380
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400381#endif
382
383
384struct image_loader {
Kristian Høgsbergb71302e2012-05-10 12:28:35 -0400385 unsigned char header[4];
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400386 int header_size;
387 pixman_image_t *(*load)(FILE *fp);
388};
389
390static const struct image_loader loaders[] = {
391 { { 0x89, 'P', 'N', 'G' }, 4, load_png },
392 { { 0xff, 0xd8 }, 2, load_jpeg },
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400393 { { 'R', 'I', 'F', 'F' }, 4, load_webp }
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400394};
395
396pixman_image_t *
397load_image(const char *filename)
398{
Quentin Glidic9ba15462016-07-10 11:00:51 +0200399 pixman_image_t *image = NULL;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400400 unsigned char header[4];
401 FILE *fp;
Kristian Høgsbergee4b4cb2012-04-11 09:43:53 -0400402 unsigned int i;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400403
Bill Spitzak28371f72014-08-19 18:13:10 -0700404 if (!filename || !*filename)
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400405 return NULL;
406
Bill Spitzak28371f72014-08-19 18:13:10 -0700407 fp = fopen(filename, "rb");
408 if (!fp) {
409 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
410 return NULL;
411 }
412
Martin Olsson712f5f42012-07-08 03:03:41 +0200413 if (fread(header, sizeof header, 1, fp) != 1) {
414 fclose(fp);
Bill Spitzak28371f72014-08-19 18:13:10 -0700415 fprintf(stderr, "%s: unable to read file header\n", filename);
Jonas Ådahl3685c3a2012-03-30 23:10:27 +0200416 return NULL;
Martin Olsson712f5f42012-07-08 03:03:41 +0200417 }
Jonas Ådahl3685c3a2012-03-30 23:10:27 +0200418
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400419 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)) {
Bill Spitzak28371f72014-08-19 18:13:10 -0700431 fprintf(stderr, "%s: unrecognized file header "
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400432 "0x%02x 0x%02x 0x%02x 0x%02x\n",
433 filename, header[0], header[1], header[2], header[3]);
Bill Spitzak28371f72014-08-19 18:13:10 -0700434 } else if (!image) {
435 /* load probably printed something, but just in case */
436 fprintf(stderr, "%s: error reading image\n", filename);
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400437 }
438
439 return image;
440}