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