~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to cairo/src/cairo-png.c

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* cairo - a vector graphics library with display and print output
 
2
 *
 
3
 * Copyright Ā© 2003 University of Southern California
 
4
 *
 
5
 * This library is free software; you can redistribute it and/or
 
6
 * modify it either under the terms of the GNU Lesser General Public
 
7
 * License version 2.1 as published by the Free Software Foundation
 
8
 * (the "LGPL") or, at your option, under the terms of the Mozilla
 
9
 * Public License Version 1.1 (the "MPL"). If you do not alter this
 
10
 * notice, a recipient may use your version of this file under either
 
11
 * the MPL or the LGPL.
 
12
 *
 
13
 * You should have received a copy of the LGPL along with this library
 
14
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
 
15
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
16
 * You should have received a copy of the MPL along with this library
 
17
 * in the file COPYING-MPL-1.1
 
18
 *
 
19
 * The contents of this file are subject to the Mozilla Public License
 
20
 * Version 1.1 (the "License"); you may not use this file except in
 
21
 * compliance with the License. You may obtain a copy of the License at
 
22
 * http://www.mozilla.org/MPL/
 
23
 *
 
24
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
 
25
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
 
26
 * the specific language governing rights and limitations.
 
27
 *
 
28
 * The Original Code is the cairo graphics library.
 
29
 *
 
30
 * The Initial Developer of the Original Code is University of Southern
 
31
 * California.
 
32
 *
 
33
 * Contributor(s):
 
34
 *      Carl D. Worth <cworth@cworth.org>
 
35
 *      Kristian HĆøgsberg <krh@redhat.com>
 
36
 *      Chris Wilson <chris@chris-wilson.co.uk>
 
37
 */
 
38
 
 
39
#include "cairoint.h"
 
40
 
 
41
#include <errno.h>
 
42
#include <png.h>
 
43
 
 
44
/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
 
45
static void
 
46
unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
 
47
{
 
48
    unsigned int i;
 
49
 
 
50
    for (i = 0; i < row_info->rowbytes; i += 4) {
 
51
        uint8_t *b = &data[i];
 
52
        uint32_t pixel;
 
53
        uint8_t  alpha;
 
54
 
 
55
        memcpy (&pixel, b, sizeof (uint32_t));
 
56
        alpha = (pixel & 0xff000000) >> 24;
 
57
        if (alpha == 0) {
 
58
            b[0] = b[1] = b[2] = b[3] = 0;
 
59
        } else {
 
60
            b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
 
61
            b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
 
62
            b[2] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
 
63
            b[3] = alpha;
 
64
        }
 
65
    }
 
66
}
 
67
 
 
68
/* Converts native endian xRGB => RGBx bytes */
 
69
static void
 
70
convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
 
71
{
 
72
    unsigned int i;
 
73
 
 
74
    for (i = 0; i < row_info->rowbytes; i += 4) {
 
75
        uint8_t *b = &data[i];
 
76
        uint32_t pixel;
 
77
 
 
78
        memcpy (&pixel, b, sizeof (uint32_t));
 
79
 
 
80
        b[0] = (pixel & 0xff0000) >> 16;
 
81
        b[1] = (pixel & 0x00ff00) >>  8;
 
82
        b[2] = (pixel & 0x0000ff) >>  0;
 
83
        b[3] = 0;
 
84
    }
 
85
}
 
86
 
 
87
/* Use a couple of simple error callbacks that do not print anything to
 
88
 * stderr and rely on the user to check for errors via the #cairo_status_t
 
89
 * return.
 
90
 */
 
91
static void
 
92
png_simple_error_callback (png_structp png,
 
93
                           png_const_charp error_msg)
 
94
{
 
95
    cairo_status_t *error = png_get_error_ptr (png);
 
96
 
 
97
    /* default to the most likely error */
 
98
    if (*error == CAIRO_STATUS_SUCCESS)
 
99
        *error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
100
 
 
101
#ifdef PNG_SETJMP_SUPPORTED
 
102
    longjmp (png_jmpbuf (png), 1);
 
103
#endif
 
104
 
 
105
    /* if we get here, then we have to choice but to abort ... */
 
106
}
 
107
 
 
108
static void
 
109
png_simple_warning_callback (png_structp png,
 
110
                             png_const_charp error_msg)
 
111
{
 
112
    cairo_status_t *error = png_get_error_ptr (png);
 
113
 
 
114
    /* default to the most likely error */
 
115
    if (*error == CAIRO_STATUS_SUCCESS)
 
116
        *error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
117
 
 
118
    /* png does not expect to abort and will try to tidy up after a warning */
 
119
}
 
120
 
 
121
 
 
122
/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn.
 
123
 * Otherwise, we will segfault if we are writing to a stream. */
 
124
static void
 
125
png_simple_output_flush_fn (png_structp png_ptr)
 
126
{
 
127
}
 
128
 
 
129
static cairo_status_t
 
130
write_png (cairo_surface_t      *surface,
 
131
           png_rw_ptr           write_func,
 
132
           void                 *closure)
 
133
{
 
134
    int i;
 
135
    cairo_status_t status;
 
136
    cairo_image_surface_t *image;
 
137
    void *image_extra;
 
138
    png_struct *png;
 
139
    png_info *info;
 
140
    png_byte **volatile rows = NULL;
 
141
    png_color_16 white;
 
142
    int png_color_type;
 
143
    int depth;
 
144
 
 
145
    status = _cairo_surface_acquire_source_image (surface,
 
146
                                                  &image,
 
147
                                                  &image_extra);
 
148
 
 
149
    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
 
150
        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
151
    else if (status)
 
152
        return status;
 
153
 
 
154
    /* PNG complains about "Image width or height is zero in IHDR" */
 
155
    if (image->width == 0 || image->height == 0) {
 
156
        status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
 
157
        goto BAIL1;
 
158
    }
 
159
 
 
160
    rows = _cairo_malloc_ab (image->height, sizeof (png_byte*));
 
161
    if (rows == NULL) {
 
162
        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
163
        goto BAIL1;
 
164
    }
 
165
 
 
166
    for (i = 0; i < image->height; i++)
 
167
        rows[i] = (png_byte *) image->data + i * image->stride;
 
168
 
 
169
    png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status,
 
170
                                   png_simple_error_callback,
 
171
                                   png_simple_warning_callback);
 
172
    if (png == NULL) {
 
173
        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
174
        goto BAIL2;
 
175
    }
 
176
 
 
177
    info = png_create_info_struct (png);
 
178
    if (info == NULL) {
 
179
        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
180
        goto BAIL3;
 
181
    }
 
182
 
 
183
#ifdef PNG_SETJMP_SUPPORTED
 
184
    if (setjmp (png_jmpbuf (png)))
 
185
        goto BAIL3;
 
186
#endif
 
187
 
 
188
    png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn);
 
189
 
 
190
    switch (image->format) {
 
191
    case CAIRO_FORMAT_ARGB32:
 
192
        depth = 8;
 
193
        png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
 
194
        break;
 
195
    case CAIRO_FORMAT_RGB24:
 
196
        depth = 8;
 
197
        png_color_type = PNG_COLOR_TYPE_RGB;
 
198
        break;
 
199
    case CAIRO_FORMAT_A8:
 
200
        depth = 8;
 
201
        png_color_type = PNG_COLOR_TYPE_GRAY;
 
202
        break;
 
203
    case CAIRO_FORMAT_A1:
 
204
        depth = 1;
 
205
        png_color_type = PNG_COLOR_TYPE_GRAY;
 
206
#ifndef WORDS_BIGENDIAN
 
207
        png_set_packswap (png);
 
208
#endif
 
209
        break;
 
210
    default:
 
211
        status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
 
212
        goto BAIL3;
 
213
    }
 
214
 
 
215
    png_set_IHDR (png, info,
 
216
                  image->width,
 
217
                  image->height, depth,
 
218
                  png_color_type,
 
219
                  PNG_INTERLACE_NONE,
 
220
                  PNG_COMPRESSION_TYPE_DEFAULT,
 
221
                  PNG_FILTER_TYPE_DEFAULT);
 
222
 
 
223
    white.gray = (1 << depth) - 1;
 
224
    white.red = white.blue = white.green = white.gray;
 
225
    png_set_bKGD (png, info, &white);
 
226
 
 
227
    if (0) { /* XXX extract meta-data from surface (i.e. creation date) */
 
228
        png_time pt;
 
229
 
 
230
        png_convert_from_time_t (&pt, time (NULL));
 
231
        png_set_tIME (png, info, &pt);
 
232
    }
 
233
 
 
234
    /* We have to call png_write_info() before setting up the write
 
235
     * transformation, since it stores data internally in 'png'
 
236
     * that is needed for the write transformation functions to work.
 
237
     */
 
238
    png_write_info (png, info);
 
239
 
 
240
    if (image->format == CAIRO_FORMAT_ARGB32)
 
241
        png_set_write_user_transform_fn (png, unpremultiply_data);
 
242
    else if (image->format == CAIRO_FORMAT_RGB24)
 
243
        png_set_write_user_transform_fn (png, convert_data_to_bytes);
 
244
    if (image->format == CAIRO_FORMAT_RGB24)
 
245
        png_set_filler (png, 0, PNG_FILLER_AFTER);
 
246
 
 
247
    png_write_image (png, rows);
 
248
    png_write_end (png, info);
 
249
 
 
250
BAIL3:
 
251
    png_destroy_write_struct (&png, &info);
 
252
BAIL2:
 
253
    free (rows);
 
254
BAIL1:
 
255
    _cairo_surface_release_source_image (surface, image, image_extra);
 
256
 
 
257
    return status;
 
258
}
 
259
 
 
260
static void
 
261
stdio_write_func (png_structp png, png_bytep data, png_size_t size)
 
262
{
 
263
    FILE *fp;
 
264
 
 
265
    fp = png_get_io_ptr (png);
 
266
    while (size) {
 
267
        size_t ret = fwrite (data, 1, size, fp);
 
268
        size -= ret;
 
269
        data += ret;
 
270
        if (size && ferror (fp)) {
 
271
            cairo_status_t *error = png_get_error_ptr (png);
 
272
            if (*error == CAIRO_STATUS_SUCCESS)
 
273
                *error = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
 
274
            png_error (png, NULL);
 
275
        }
 
276
    }
 
277
}
 
278
 
 
279
/**
 
280
 * cairo_surface_write_to_png:
 
281
 * @surface: a #cairo_surface_t with pixel contents
 
282
 * @filename: the name of a file to write to
 
283
 *
 
284
 * Writes the contents of @surface to a new file @filename as a PNG
 
285
 * image.
 
286
 *
 
287
 * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
 
288
 * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not
 
289
 * be allocated for the operation or
 
290
 * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
 
291
 * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs
 
292
 * while attempting to write the file.
 
293
 **/
 
294
cairo_status_t
 
295
cairo_surface_write_to_png (cairo_surface_t     *surface,
 
296
                            const char          *filename)
 
297
{
 
298
    FILE *fp;
 
299
    cairo_status_t status;
 
300
 
 
301
    if (surface->status)
 
302
        return surface->status;
 
303
 
 
304
    if (surface->finished)
 
305
        return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
306
 
 
307
    fp = fopen (filename, "wb");
 
308
    if (fp == NULL) {
 
309
        switch (errno) {
 
310
        case ENOMEM:
 
311
            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
312
        default:
 
313
            return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
 
314
        }
 
315
    }
 
316
 
 
317
    status = write_png (surface, stdio_write_func, fp);
 
318
 
 
319
    if (fclose (fp) && status == CAIRO_STATUS_SUCCESS)
 
320
        status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
 
321
 
 
322
    return status;
 
323
}
 
324
 
 
325
struct png_write_closure_t {
 
326
    cairo_write_func_t           write_func;
 
327
    void                        *closure;
 
328
};
 
329
 
 
330
static void
 
331
stream_write_func (png_structp png, png_bytep data, png_size_t size)
 
332
{
 
333
    cairo_status_t status;
 
334
    struct png_write_closure_t *png_closure;
 
335
 
 
336
    png_closure = png_get_io_ptr (png);
 
337
    status = png_closure->write_func (png_closure->closure, data, size);
 
338
    if (status) {
 
339
        cairo_status_t *error = png_get_error_ptr (png);
 
340
        if (*error == CAIRO_STATUS_SUCCESS)
 
341
            *error = status;
 
342
        png_error (png, NULL);
 
343
    }
 
344
}
 
345
 
 
346
/**
 
347
 * cairo_surface_write_to_png_stream:
 
348
 * @surface: a #cairo_surface_t with pixel contents
 
349
 * @write_func: a #cairo_write_func_t
 
350
 * @closure: closure data for the write function
 
351
 *
 
352
 * Writes the image surface to the write function.
 
353
 *
 
354
 * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
 
355
 * successfully.  Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if
 
356
 * memory could not be allocated for the operation,
 
357
 * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
 
358
 * pixel contents.
 
359
 **/
 
360
cairo_status_t
 
361
cairo_surface_write_to_png_stream (cairo_surface_t      *surface,
 
362
                                   cairo_write_func_t   write_func,
 
363
                                   void                 *closure)
 
364
{
 
365
    struct png_write_closure_t png_closure;
 
366
 
 
367
    if (surface->status)
 
368
        return surface->status;
 
369
 
 
370
    if (surface->finished)
 
371
        return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
372
 
 
373
    png_closure.write_func = write_func;
 
374
    png_closure.closure = closure;
 
375
 
 
376
    return write_png (surface, stream_write_func, &png_closure);
 
377
}
 
378
slim_hidden_def (cairo_surface_write_to_png_stream);
 
379
 
 
380
static inline int
 
381
multiply_alpha (int alpha, int color)
 
382
{
 
383
    int temp = (alpha * color) + 0x80;
 
384
    return ((temp + (temp >> 8)) >> 8);
 
385
}
 
386
 
 
387
/* Premultiplies data and converts RGBA bytes => native endian */
 
388
static void
 
389
premultiply_data (png_structp   png,
 
390
                  png_row_infop row_info,
 
391
                  png_bytep     data)
 
392
{
 
393
    unsigned int i;
 
394
 
 
395
    for (i = 0; i < row_info->rowbytes; i += 4) {
 
396
        uint8_t *base  = &data[i];
 
397
        uint8_t  alpha = base[3];
 
398
        uint32_t p;
 
399
 
 
400
        if (alpha == 0) {
 
401
            p = 0;
 
402
        } else {
 
403
            uint8_t  red   = base[0];
 
404
            uint8_t  green = base[1];
 
405
            uint8_t  blue  = base[2];
 
406
 
 
407
            if (alpha != 0xff) {
 
408
                red   = multiply_alpha (alpha, red);
 
409
                green = multiply_alpha (alpha, green);
 
410
                blue  = multiply_alpha (alpha, blue);
 
411
            }
 
412
            p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
 
413
        }
 
414
        memcpy (base, &p, sizeof (uint32_t));
 
415
    }
 
416
}
 
417
 
 
418
/* Converts RGBx bytes to native endian xRGB */
 
419
static void
 
420
convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
 
421
{
 
422
    unsigned int i;
 
423
 
 
424
    for (i = 0; i < row_info->rowbytes; i += 4) {
 
425
        uint8_t *base  = &data[i];
 
426
        uint8_t  red   = base[0];
 
427
        uint8_t  green = base[1];
 
428
        uint8_t  blue  = base[2];
 
429
        uint32_t pixel;
 
430
 
 
431
        pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0);
 
432
        memcpy (base, &pixel, sizeof (uint32_t));
 
433
    }
 
434
}
 
435
 
 
436
static cairo_surface_t *
 
437
read_png (png_rw_ptr    read_func,
 
438
          void          *closure)
 
439
{
 
440
    cairo_surface_t *surface;
 
441
    png_struct *png = NULL;
 
442
    png_info *info;
 
443
    png_byte *data = NULL;
 
444
    png_byte **row_pointers = NULL;
 
445
    png_uint_32 png_width, png_height;
 
446
    int depth, color_type, interlace, stride;
 
447
    unsigned int i;
 
448
    cairo_format_t format;
 
449
    cairo_status_t status;
 
450
 
 
451
    /* XXX: Perhaps we'll want some other error handlers? */
 
452
    png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
 
453
                                  &status,
 
454
                                  png_simple_error_callback,
 
455
                                  png_simple_warning_callback);
 
456
    if (png == NULL) {
 
457
        surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
458
        goto BAIL;
 
459
    }
 
460
 
 
461
    info = png_create_info_struct (png);
 
462
    if (info == NULL) {
 
463
        surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
464
        goto BAIL;
 
465
    }
 
466
 
 
467
    png_set_read_fn (png, closure, read_func);
 
468
 
 
469
    status = CAIRO_STATUS_SUCCESS;
 
470
#ifdef PNG_SETJMP_SUPPORTED
 
471
    if (setjmp (png_jmpbuf (png))) {
 
472
        surface = _cairo_surface_create_in_error (status);
 
473
        goto BAIL;
 
474
    }
 
475
#endif
 
476
 
 
477
    png_read_info (png, info);
 
478
 
 
479
    png_get_IHDR (png, info,
 
480
                  &png_width, &png_height, &depth,
 
481
                  &color_type, &interlace, NULL, NULL);
 
482
    if (status) { /* catch any early warnings */
 
483
        surface = _cairo_surface_create_in_error (status);
 
484
        goto BAIL;
 
485
    }
 
486
 
 
487
    /* convert palette/gray image to rgb */
 
488
    if (color_type == PNG_COLOR_TYPE_PALETTE)
 
489
        png_set_palette_to_rgb (png);
 
490
 
 
491
    /* expand gray bit depth if needed */
 
492
    if (color_type == PNG_COLOR_TYPE_GRAY) {
 
493
#if PNG_LIBPNG_VER >= 10209
 
494
        png_set_expand_gray_1_2_4_to_8 (png);
 
495
#else
 
496
        png_set_gray_1_2_4_to_8 (png);
 
497
#endif
 
498
    }
 
499
 
 
500
    /* transform transparency to alpha */
 
501
    if (png_get_valid (png, info, PNG_INFO_tRNS))
 
502
        png_set_tRNS_to_alpha (png);
 
503
 
 
504
    if (depth == 16)
 
505
        png_set_strip_16 (png);
 
506
 
 
507
    if (depth < 8)
 
508
        png_set_packing (png);
 
509
 
 
510
    /* convert grayscale to RGB */
 
511
    if (color_type == PNG_COLOR_TYPE_GRAY ||
 
512
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
 
513
    {
 
514
        png_set_gray_to_rgb (png);
 
515
    }
 
516
 
 
517
    if (interlace != PNG_INTERLACE_NONE)
 
518
        png_set_interlace_handling (png);
 
519
 
 
520
    png_set_filler (png, 0xff, PNG_FILLER_AFTER);
 
521
 
 
522
    /* recheck header after setting EXPAND options */
 
523
    png_read_update_info (png, info);
 
524
    png_get_IHDR (png, info,
 
525
                  &png_width, &png_height, &depth,
 
526
                  &color_type, &interlace, NULL, NULL);
 
527
    if (depth != 8 ||
 
528
        ! (color_type == PNG_COLOR_TYPE_RGB ||
 
529
           color_type == PNG_COLOR_TYPE_RGB_ALPHA))
 
530
    {
 
531
        surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR));
 
532
        goto BAIL;
 
533
    }
 
534
 
 
535
    switch (color_type) {
 
536
        default:
 
537
            ASSERT_NOT_REACHED;
 
538
            /* fall-through just in case ;-) */
 
539
 
 
540
        case PNG_COLOR_TYPE_RGB_ALPHA:
 
541
            format = CAIRO_FORMAT_ARGB32;
 
542
            png_set_read_user_transform_fn (png, premultiply_data);
 
543
            break;
 
544
 
 
545
        case PNG_COLOR_TYPE_RGB:
 
546
            format = CAIRO_FORMAT_RGB24;
 
547
            png_set_read_user_transform_fn (png, convert_bytes_to_data);
 
548
            break;
 
549
    }
 
550
 
 
551
    stride = cairo_format_stride_for_width (format, png_width);
 
552
    if (stride < 0) {
 
553
        surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
 
554
        goto BAIL;
 
555
    }
 
556
 
 
557
    data = _cairo_malloc_ab (png_height, stride);
 
558
    if (data == NULL) {
 
559
        surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
560
        goto BAIL;
 
561
    }
 
562
 
 
563
    row_pointers = _cairo_malloc_ab (png_height, sizeof (char *));
 
564
    if (row_pointers == NULL) {
 
565
        surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
566
        goto BAIL;
 
567
    }
 
568
 
 
569
    for (i = 0; i < png_height; i++)
 
570
        row_pointers[i] = &data[i * stride];
 
571
 
 
572
    png_read_image (png, row_pointers);
 
573
    png_read_end (png, info);
 
574
 
 
575
    if (status) { /* catch any late warnings - probably hit an error already */
 
576
        surface = _cairo_surface_create_in_error (status);
 
577
        goto BAIL;
 
578
    }
 
579
 
 
580
    surface = cairo_image_surface_create_for_data (data, format,
 
581
                                                   png_width, png_height,
 
582
                                                   stride);
 
583
    if (surface->status)
 
584
        goto BAIL;
 
585
 
 
586
    _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface);
 
587
    data = NULL;
 
588
 
 
589
 BAIL:
 
590
    if (row_pointers)
 
591
        free (row_pointers);
 
592
    if (data)
 
593
        free (data);
 
594
    if (png)
 
595
        png_destroy_read_struct (&png, &info, NULL);
 
596
 
 
597
    return surface;
 
598
}
 
599
 
 
600
static void
 
601
stdio_read_func (png_structp png, png_bytep data, png_size_t size)
 
602
{
 
603
    FILE *fp;
 
604
 
 
605
    fp = png_get_io_ptr (png);
 
606
    while (size) {
 
607
        size_t ret = fread (data, 1, size, fp);
 
608
        size -= ret;
 
609
        data += ret;
 
610
        if (size && (feof (fp) || ferror (fp))) {
 
611
            cairo_status_t *error = png_get_error_ptr (png);
 
612
            if (*error == CAIRO_STATUS_SUCCESS)
 
613
                *error = _cairo_error (CAIRO_STATUS_READ_ERROR);
 
614
            png_error (png, NULL);
 
615
        }
 
616
    }
 
617
}
 
618
 
 
619
/**
 
620
 * cairo_image_surface_create_from_png:
 
621
 * @filename: name of PNG file to load
 
622
 *
 
623
 * Creates a new image surface and initializes the contents to the
 
624
 * given PNG file.
 
625
 *
 
626
 * Return value: a new #cairo_surface_t initialized with the contents
 
627
 * of the PNG file, or a "nil" surface if any error occurred. A nil
 
628
 * surface can be checked for with cairo_surface_status(surface) which
 
629
 * may return one of the following values:
 
630
 *
 
631
 *      %CAIRO_STATUS_NO_MEMORY
 
632
 *      %CAIRO_STATUS_FILE_NOT_FOUND
 
633
 *      %CAIRO_STATUS_READ_ERROR
 
634
 **/
 
635
cairo_surface_t *
 
636
cairo_image_surface_create_from_png (const char *filename)
 
637
{
 
638
    FILE *fp;
 
639
    cairo_surface_t *surface;
 
640
 
 
641
    fp = fopen (filename, "rb");
 
642
    if (fp == NULL) {
 
643
        cairo_status_t status;
 
644
        switch (errno) {
 
645
        case ENOMEM:
 
646
            status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
647
            break;
 
648
        case ENOENT:
 
649
            status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
 
650
            break;
 
651
        default:
 
652
            status = _cairo_error (CAIRO_STATUS_READ_ERROR);
 
653
            break;
 
654
        }
 
655
        return _cairo_surface_create_in_error (status);
 
656
    }
 
657
 
 
658
    surface = read_png (stdio_read_func, fp);
 
659
 
 
660
    fclose (fp);
 
661
 
 
662
    return surface;
 
663
}
 
664
 
 
665
struct png_read_closure_t {
 
666
    cairo_read_func_t            read_func;
 
667
    void                        *closure;
 
668
};
 
669
 
 
670
static void
 
671
stream_read_func (png_structp png, png_bytep data, png_size_t size)
 
672
{
 
673
    cairo_status_t status;
 
674
    struct png_read_closure_t *png_closure;
 
675
 
 
676
    png_closure = png_get_io_ptr (png);
 
677
    status = png_closure->read_func (png_closure->closure, data, size);
 
678
    if (status) {
 
679
        cairo_status_t *error = png_get_error_ptr (png);
 
680
        if (*error == CAIRO_STATUS_SUCCESS)
 
681
            *error = status;
 
682
        png_error (png, NULL);
 
683
    }
 
684
}
 
685
 
 
686
/**
 
687
 * cairo_image_surface_create_from_png_stream:
 
688
 * @read_func: function called to read the data of the file
 
689
 * @closure: data to pass to @read_func.
 
690
 *
 
691
 * Creates a new image surface from PNG data read incrementally
 
692
 * via the @read_func function.
 
693
 *
 
694
 * Return value: a new #cairo_surface_t initialized with the contents
 
695
 * of the PNG file or %NULL if the data read is not a valid PNG image or
 
696
 * memory could not be allocated for the operation.
 
697
 **/
 
698
cairo_surface_t *
 
699
cairo_image_surface_create_from_png_stream (cairo_read_func_t   read_func,
 
700
                                            void                *closure)
 
701
{
 
702
    struct png_read_closure_t png_closure;
 
703
 
 
704
    png_closure.read_func = read_func;
 
705
    png_closure.closure = closure;
 
706
 
 
707
    return read_png (stream_read_func, &png_closure);
 
708
}