blob: 4257590a202c7b37d0fefb9adf152ff8ae7f7db6 [file] [log] [blame]
Kristian Høgsbergffd710e2008-12-02 15:15:01 -05001/*
2 * Copyright © 2008 Kristian Høgsberg
Kristian Høgsberg902865c2012-02-08 10:11:42 -05003 * Copyright © 2012 Intel Corporation
Kristian Høgsbergffd710e2008-12-02 15:15:01 -05004 *
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
Kristian Høgsberg3d5437c2012-02-08 12:46:57 -050024#include "../config.h"
25
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050026#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050030#include <math.h>
31#include <cairo.h>
32#include "cairo-util.h"
33
Kristian Høgsberg27d38662011-10-20 13:11:12 -040034#include <jpeglib.h>
Kristian Høgsbergd6548762012-01-25 15:43:48 -050035#include <png.h>
Kristian Høgsberg27d38662011-10-20 13:11:12 -040036
Kristian Høgsberg3d5437c2012-02-08 12:46:57 -050037#ifdef HAVE_WEBP
38#include <webp/decode.h>
39#endif
40
Kristian Høgsberg87330262008-11-17 22:23:55 -050041#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
42
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050043void
Benjamin Franzke47eb8f42011-10-07 09:08:56 +020044surface_flush_device(cairo_surface_t *surface)
45{
46 cairo_device_t *device;
47
48 device = cairo_surface_get_device(surface);
49 if (device)
50 cairo_device_flush(device);
51}
52
53void
Kristian Høgsberg10bdd292008-11-08 23:27:27 -050054blur_surface(cairo_surface_t *surface, int margin)
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050055{
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050056 int32_t width, height, stride, x, y, z, w;
57 uint8_t *src, *dst;
58 uint32_t *s, *d, a, p;
Kristian Høgsberg87330262008-11-17 22:23:55 -050059 int i, j, k, size, half;
Kristian Høgsberg49e868c2010-06-15 16:18:58 -040060 uint32_t kernel[49];
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050061 double f;
62
Kristian Høgsberg87330262008-11-17 22:23:55 -050063 size = ARRAY_LENGTH(kernel);
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050064 width = cairo_image_surface_get_width(surface);
65 height = cairo_image_surface_get_height(surface);
66 stride = cairo_image_surface_get_stride(surface);
67 src = cairo_image_surface_get_data(surface);
68
Kristian Høgsberg5fc96ff2009-09-12 15:58:48 -040069 dst = malloc(height * stride);
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050070
71 half = size / 2;
Kristian Høgsberg0cd8f6e2011-01-21 22:19:40 -050072 a = 0;
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050073 for (i = 0; i < size; i++) {
74 f = (i - half);
Kristian Høgsberg49e868c2010-06-15 16:18:58 -040075 kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000;
Kristian Høgsberg0cd8f6e2011-01-21 22:19:40 -050076 a += kernel[i];
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050077 }
78
79 for (i = 0; i < height; i++) {
80 s = (uint32_t *) (src + i * stride);
81 d = (uint32_t *) (dst + i * stride);
82 for (j = 0; j < width; j++) {
Kristian Høgsberg87330262008-11-17 22:23:55 -050083 if (margin < j && j < width - margin) {
84 d[j] = s[j];
Kristian Høgsberg10bdd292008-11-08 23:27:27 -050085 continue;
Kristian Høgsberg87330262008-11-17 22:23:55 -050086 }
87
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -050088 x = 0;
89 y = 0;
90 z = 0;
91 w = 0;
92 for (k = 0; k < size; k++) {
93 if (j - half + k < 0 || j - half + k >= width)
94 continue;
95 p = s[j - half + k];
96
97 x += (p >> 24) * kernel[k];
98 y += ((p >> 16) & 0xff) * kernel[k];
99 z += ((p >> 8) & 0xff) * kernel[k];
100 w += (p & 0xff) * kernel[k];
101 }
102 d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
103 }
104 }
105
106 for (i = 0; i < height; i++) {
107 s = (uint32_t *) (dst + i * stride);
108 d = (uint32_t *) (src + i * stride);
109 for (j = 0; j < width; j++) {
Kristian Høgsberg87330262008-11-17 22:23:55 -0500110 if (margin <= i && i < height - margin) {
111 d[j] = s[j];
Kristian Høgsberg10bdd292008-11-08 23:27:27 -0500112 continue;
Kristian Høgsberg87330262008-11-17 22:23:55 -0500113 }
114
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -0500115 x = 0;
116 y = 0;
117 z = 0;
118 w = 0;
119 for (k = 0; k < size; k++) {
120 if (i - half + k < 0 || i - half + k >= height)
121 continue;
122 s = (uint32_t *) (dst + (i - half + k) * stride);
123 p = s[j];
124
125 x += (p >> 24) * kernel[k];
126 y += ((p >> 16) & 0xff) * kernel[k];
127 z += ((p >> 8) & 0xff) * kernel[k];
128 w += (p & 0xff) * kernel[k];
129 }
130 d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
131 }
132 }
133
Kristian Høgsberg5fc96ff2009-09-12 15:58:48 -0400134 free(dst);
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400135 cairo_surface_mark_dirty(surface);
136}
137
138void
139tile_mask(cairo_t *cr, cairo_surface_t *surface,
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400140 int x, int y, int width, int height, int margin, int top_margin)
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400141{
142 cairo_pattern_t *pattern;
143 cairo_matrix_t matrix;
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400144 int i, fx, fy, vmargin;
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400145
146 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
147 pattern = cairo_pattern_create_for_surface (surface);
148
149 for (i = 0; i < 4; i++) {
150 fx = i & 1;
151 fy = i >> 1;
152
153 cairo_matrix_init_translate(&matrix,
154 -x + fx * (128 - width),
155 -y + fy * (128 - height));
156 cairo_pattern_set_matrix(pattern, &matrix);
157
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400158 if (fy)
159 vmargin = margin;
160 else
161 vmargin = top_margin;
162
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400163 cairo_reset_clip(cr);
164 cairo_rectangle(cr,
165 x + fx * (width - margin),
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400166 y + fy * (height - vmargin),
167 margin, vmargin);
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400168 cairo_clip (cr);
169 cairo_mask(cr, pattern);
170 }
171
Tiago Vignatti0a266412012-02-09 19:06:56 +0200172 /* Top stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400173 cairo_matrix_init_translate(&matrix, 64, 0);
174 cairo_matrix_scale(&matrix, 64.0 / (width - 2 * margin), 1);
175 cairo_matrix_translate(&matrix, -x - width / 2, -y);
176 cairo_pattern_set_matrix(pattern, &matrix);
177 cairo_rectangle(cr, x + margin, y, width - 2 * margin, margin);
178
179 cairo_reset_clip(cr);
180 cairo_rectangle(cr,
181 x + margin,
182 y,
183 width - 2 * margin, margin);
184 cairo_clip (cr);
185 cairo_mask(cr, pattern);
186
Tiago Vignatti0a266412012-02-09 19:06:56 +0200187 /* Bottom stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400188 cairo_matrix_translate(&matrix, 0, -height + 128);
189 cairo_pattern_set_matrix(pattern, &matrix);
190
191 cairo_reset_clip(cr);
192 cairo_rectangle(cr, x + margin, y + height - margin,
193 width - 2 * margin, margin);
194 cairo_clip (cr);
195 cairo_mask(cr, pattern);
196
Tiago Vignatti0a266412012-02-09 19:06:56 +0200197 /* Left stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400198 cairo_matrix_init_translate(&matrix, 0, 64);
199 cairo_matrix_scale(&matrix, 1, 64.0 / (height - 2 * margin));
200 cairo_matrix_translate(&matrix, -x, -y - height / 2);
201 cairo_pattern_set_matrix(pattern, &matrix);
202 cairo_reset_clip(cr);
203 cairo_rectangle(cr, x, y + margin, margin, height - 2 * margin);
204 cairo_clip (cr);
205 cairo_mask(cr, pattern);
206
Tiago Vignatti0a266412012-02-09 19:06:56 +0200207 /* Right stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400208 cairo_matrix_translate(&matrix, -width + 128, 0);
209 cairo_pattern_set_matrix(pattern, &matrix);
210 cairo_rectangle(cr, x + width - margin, y + margin,
211 margin, height - 2 * margin);
212 cairo_reset_clip(cr);
213 cairo_clip (cr);
214 cairo_mask(cr, pattern);
215
216 cairo_pattern_destroy(pattern);
217 cairo_reset_clip(cr);
218}
219
220void
221tile_source(cairo_t *cr, cairo_surface_t *surface,
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400222 int x, int y, int width, int height, int margin, int top_margin)
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400223{
224 cairo_pattern_t *pattern;
225 cairo_matrix_t matrix;
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400226 int i, fx, fy, vmargin;
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400227
228 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
229 pattern = cairo_pattern_create_for_surface (surface);
230 cairo_set_source(cr, pattern);
231 cairo_pattern_destroy(pattern);
232
233 for (i = 0; i < 4; i++) {
234 fx = i & 1;
235 fy = i >> 1;
236
237 cairo_matrix_init_translate(&matrix,
238 -x + fx * (128 - width),
239 -y + fy * (128 - height));
240 cairo_pattern_set_matrix(pattern, &matrix);
241
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400242 if (fy)
243 vmargin = margin;
244 else
245 vmargin = top_margin;
246
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400247 cairo_rectangle(cr,
248 x + fx * (width - margin),
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400249 y + fy * (height - vmargin),
250 margin, vmargin);
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400251 cairo_fill(cr);
252 }
253
Tiago Vignatti0a266412012-02-09 19:06:56 +0200254 /* Top stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400255 cairo_matrix_init_translate(&matrix, 64, 0);
256 cairo_matrix_scale(&matrix, 64.0 / (width - 2 * margin), 1);
257 cairo_matrix_translate(&matrix, -x - width / 2, -y);
258 cairo_pattern_set_matrix(pattern, &matrix);
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400259 cairo_rectangle(cr, x + margin, y, width - 2 * margin, top_margin);
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400260 cairo_fill(cr);
261
Tiago Vignatti0a266412012-02-09 19:06:56 +0200262 /* Bottom stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400263 cairo_matrix_translate(&matrix, 0, -height + 128);
264 cairo_pattern_set_matrix(pattern, &matrix);
265 cairo_rectangle(cr, x + margin, y + height - margin,
266 width - 2 * margin, margin);
267 cairo_fill(cr);
268
Tiago Vignatti0a266412012-02-09 19:06:56 +0200269 /* Left stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400270 cairo_matrix_init_translate(&matrix, 0, 64);
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400271 cairo_matrix_scale(&matrix, 1, 64.0 / (height - margin - top_margin));
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400272 cairo_matrix_translate(&matrix, -x, -y - height / 2);
273 cairo_pattern_set_matrix(pattern, &matrix);
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400274 cairo_rectangle(cr, x, y + top_margin,
275 margin, height - margin - top_margin);
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400276 cairo_fill(cr);
277
Tiago Vignatti0a266412012-02-09 19:06:56 +0200278 /* Right stretch */
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400279 cairo_matrix_translate(&matrix, -width + 128, 0);
280 cairo_pattern_set_matrix(pattern, &matrix);
Kristian Høgsberg9a686242010-08-18 15:28:04 -0400281 cairo_rectangle(cr, x + width - margin, y + top_margin,
282 margin, height - margin - top_margin);
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400283 cairo_fill(cr);
Kristian Høgsberg2f2cfae2008-11-08 22:46:30 -0500284}
Kristian Høgsberg1e164b92011-09-13 14:47:46 -0400285
286void
287rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
288{
289 cairo_move_to(cr, x0, y0 + radius);
290 cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2);
291 cairo_line_to(cr, x1 - radius, y0);
292 cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI);
293 cairo_line_to(cr, x1, y1 - radius);
294 cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
295 cairo_line_to(cr, x0 + radius, y1);
296 cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
297 cairo_close_path(cr);
298}
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400299
Kristian Høgsberg02453dd2011-11-22 14:40:00 -0500300static void
301swizzle_row(JSAMPLE *row, JDIMENSION width)
302{
303 JSAMPLE *s;
304 uint32_t *d;
305
306 s = row + (width - 1) * 3;
307 d = (uint32_t *) (row + (width - 1) * 4);
308 while (s >= row) {
309 *d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0);
310 s -= 3;
311 d--;
312 }
313}
314
Kristian Høgsbergffe3bba2012-01-25 14:54:26 -0500315static void
316error_exit(j_common_ptr cinfo)
317{
318 longjmp(cinfo->client_data, 1);
319}
320
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500321static cairo_surface_t *
322load_jpeg(FILE *fp)
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400323{
324 struct jpeg_decompress_struct cinfo;
325 struct jpeg_error_mgr jerr;
Kristian Høgsberg02453dd2011-11-22 14:40:00 -0500326 int stride, i, first;
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400327 JSAMPLE *data, *rows[4];
Kristian Høgsbergffe3bba2012-01-25 14:54:26 -0500328 jmp_buf env;
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400329
330 cinfo.err = jpeg_std_error(&jerr);
Kristian Høgsbergffe3bba2012-01-25 14:54:26 -0500331 jerr.error_exit = error_exit;
332 cinfo.client_data = env;
333 if (setjmp(env))
334 return NULL;
335
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400336 jpeg_create_decompress(&cinfo);
337
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400338 jpeg_stdio_src(&cinfo, fp);
339
340 jpeg_read_header(&cinfo, TRUE);
341
Kristian Høgsberg02453dd2011-11-22 14:40:00 -0500342 cinfo.out_color_space = JCS_RGB;
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400343 jpeg_start_decompress(&cinfo);
344
345 stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
346 cinfo.output_width);
347 data = malloc(stride * cinfo.output_height);
348 if (data == NULL) {
349 fprintf(stderr, "couldn't allocate image data\n");
350 return NULL;
351 }
352
353 while (cinfo.output_scanline < cinfo.output_height) {
Kristian Høgsberg02453dd2011-11-22 14:40:00 -0500354 first = cinfo.output_scanline;
Kristian Høgsbergbf8bd5a2011-10-20 14:44:48 -0400355 for (i = 0; i < ARRAY_LENGTH(rows); i++)
Kristian Høgsberg02453dd2011-11-22 14:40:00 -0500356 rows[i] = data + (first + i) * stride;
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400357
358 jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
Kristian Høgsberg02453dd2011-11-22 14:40:00 -0500359 for (i = 0; first + i < cinfo.output_scanline; i++)
360 swizzle_row(rows[i], cinfo.output_width);
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400361 }
362
363 jpeg_finish_decompress(&cinfo);
364
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400365 jpeg_destroy_decompress(&cinfo);
366
367 return cairo_image_surface_create_for_data (data,
368 CAIRO_FORMAT_RGB24,
369 cinfo.output_width,
370 cinfo.output_height,
371 stride);
372}
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500373
374static inline int
375multiply_alpha(int alpha, int color)
376{
377 int temp = (alpha * color) + 0x80;
378
379 return ((temp + (temp >> 8)) >> 8);
380}
381
382static void
383premultiply_data(png_structp png,
384 png_row_infop row_info,
385 png_bytep data)
386{
387 unsigned int i;
388 png_bytep p;
389
390 for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
391 png_byte alpha = p[3];
392 uint32_t w;
393
394 if (alpha == 0) {
395 w = 0;
396 } else {
397 png_byte red = p[0];
398 png_byte green = p[1];
399 png_byte blue = p[2];
400
401 if (alpha != 0xff) {
402 red = multiply_alpha(alpha, red);
403 green = multiply_alpha(alpha, green);
404 blue = multiply_alpha(alpha, blue);
405 }
406 w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
407 }
408
409 * (uint32_t *) p = w;
410 }
411}
412
413static void
414read_func(png_structp png, png_bytep data, png_size_t size)
415{
416 FILE *fp = png_get_io_ptr(png);
417
418 if (fread(data, 1, size, fp) < 0)
419 png_error(png, NULL);
420}
421
422static void
423png_error_callback(png_structp png, png_const_charp error_msg)
424{
425 longjmp (png_jmpbuf (png), 1);
426}
427
428static cairo_surface_t *
429load_png(FILE *fp)
430{
431 png_struct *png;
432 png_info *info;
433 png_byte *data = NULL;
434 png_byte **row_pointers = NULL;
435 png_uint_32 width, height;
436 int depth, color_type, interlace, stride;
437 unsigned int i;
438
439 png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
440 png_error_callback, NULL);
441 if (!png)
442 return NULL;
443
444 info = png_create_info_struct(png);
445 if (!info) {
446 png_destroy_read_struct(&png, &info, NULL);
447 return NULL;
448 }
449
450 if (setjmp(png_jmpbuf(png))) {
451 if (data)
452 free(data);
453 if (row_pointers)
454 free(row_pointers);
455 png_destroy_read_struct(&png, &info, NULL);
456 return NULL;
457 }
458
459 png_set_read_fn(png, fp, read_func);
460 png_read_info(png, info);
461 png_get_IHDR(png, info,
462 &width, &height, &depth,
463 &color_type, &interlace, NULL, NULL);
464
465 if (color_type == PNG_COLOR_TYPE_PALETTE)
466 png_set_palette_to_rgb(png);
467
468 if (color_type == PNG_COLOR_TYPE_GRAY)
469 png_set_expand_gray_1_2_4_to_8(png);
470
471 if (png_get_valid(png, info, PNG_INFO_tRNS))
472 png_set_tRNS_to_alpha(png);
473
474 if (depth == 16)
475 png_set_strip_16(png);
476
477 if (depth < 8)
478 png_set_packing(png);
479
480 if (color_type == PNG_COLOR_TYPE_GRAY ||
481 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
482 png_set_gray_to_rgb(png);
483
484 if (interlace != PNG_INTERLACE_NONE)
485 png_set_interlace_handling(png);
486
487 png_set_filler(png, 0xff, PNG_FILLER_AFTER);
488 png_set_read_user_transform_fn(png, premultiply_data);
489 png_read_update_info(png, info);
490 png_get_IHDR(png, info,
491 &width, &height, &depth,
492 &color_type, &interlace, NULL, NULL);
493
494
495 stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width);
496 data = malloc(stride * height);
497 if (!data) {
498 png_destroy_read_struct(&png, &info, NULL);
499 return NULL;
500 }
501
502 row_pointers = malloc(height * sizeof row_pointers[0]);
503 if (row_pointers == NULL) {
504 free(data);
505 png_destroy_read_struct(&png, &info, NULL);
506 return NULL;
507 }
508
509 for (i = 0; i < height; i++)
510 row_pointers[i] = &data[i * stride];
511
512 png_read_image(png, row_pointers);
513 png_read_end(png, info);
514
515 free(row_pointers);
516 png_destroy_read_struct(&png, &info, NULL);
517
518 return cairo_image_surface_create_for_data (data,
519 CAIRO_FORMAT_RGB24,
520 width, height, stride);
521}
522
Kristian Høgsberg3d5437c2012-02-08 12:46:57 -0500523#ifdef HAVE_WEBP
524
525static cairo_surface_t *
526load_webp(FILE *fp)
527{
528 WebPDecoderConfig config;
529 uint8_t buffer[16 * 1024];
530 int len;
531 VP8StatusCode status;
532 WebPIDecoder *idec;
533
534 if (!WebPInitDecoderConfig(&config)) {
535 fprintf(stderr, "Library version mismatch!\n");
536 return NULL;
537 }
538
539 /* webp decoding api doesn't seem to specify a min size that's
540 usable for GetFeatures, but 256 works... */
541 len = fread(buffer, 1, 256, fp);
542 status = WebPGetFeatures(buffer, len, &config.input);
543 if (status != VP8_STATUS_OK) {
544 fprintf(stderr, "failed to parse webp header\n");
545 WebPFreeDecBuffer(&config.output);
546 return NULL;
547 }
548
549 config.output.colorspace = MODE_BGRA;
550 config.output.u.RGBA.stride =
551 cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
552 config.input.width);
553 config.output.u.RGBA.size =
554 config.output.u.RGBA.stride * config.input.height;
555 config.output.u.RGBA.rgba =
556 malloc(config.output.u.RGBA.stride * config.input.height);
557 config.output.is_external_memory = 1;
558 if (!config.output.u.RGBA.rgba) {
559 WebPFreeDecBuffer(&config.output);
560 return NULL;
561 }
562
563 rewind(fp);
564 idec = WebPINewDecoder(&config.output);
565 if (!idec) {
566 WebPFreeDecBuffer(&config.output);
567 return NULL;
568 }
569
570 while (!feof(fp)) {
571 len = fread(buffer, 1, sizeof buffer, fp);
572 status = WebPIAppend(idec, buffer, len);
573 if (status != VP8_STATUS_OK) {
574 fprintf(stderr, "webp decode status %d\n", status);
575 WebPIDelete(idec);
576 WebPFreeDecBuffer(&config.output);
577 return NULL;
578 }
579 }
580
581 WebPIDelete(idec);
582 WebPFreeDecBuffer(&config.output);
583
584 return cairo_image_surface_create_for_data (config.output.u.RGBA.rgba,
585 CAIRO_FORMAT_RGB24,
586 config.input.width,
587 config.input.height,
588 config.output.u.RGBA.stride);
589}
590
591#endif
592
593
594struct image_loader {
595 char header[4];
596 int header_size;
597 cairo_surface_t *(*load)(FILE *fp);
598};
599
600static const struct image_loader loaders[] = {
601 { { 0x89, 'P', 'N', 'G' }, 4, load_png },
602 { { 0xff, 0xd8 }, 2, load_jpeg },
603#ifdef HAVE_WEBP
604 { { 'R', 'I', 'F', 'F' }, 4, load_webp }
605#endif
606};
607
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500608cairo_surface_t *
609load_image(const char *filename)
610{
611 cairo_surface_t *surface;
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500612 unsigned char header[4];
613 FILE *fp;
Kristian Høgsberg3d5437c2012-02-08 12:46:57 -0500614 int i;
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500615
616 fp = fopen(filename, "rb");
617 if (fp == NULL)
618 return NULL;
619
620 fread(header, sizeof header, 1, fp);
621 rewind(fp);
Kristian Høgsberg3d5437c2012-02-08 12:46:57 -0500622 for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
623 if (memcmp(header, loaders[i].header,
624 loaders[i].header_size) == 0) {
625 surface = loaders[i].load(fp);
626 break;
627 }
628 }
629
630 fclose(fp);
631
632 if (i == ARRAY_LENGTH(loaders)) {
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500633 fprintf(stderr, "unrecognized file header for %s: "
634 "0x%02x 0x%02x 0x%02x 0x%02x\n",
635 filename, header[0], header[1], header[2], header[3]);
636 surface = NULL;
637 }
638
Kristian Høgsbergd6548762012-01-25 15:43:48 -0500639 return surface;
640}