Back to index

lightning-sunbird  0.9+nobinonly
cairo-png.c
Go to the documentation of this file.
00001 /* cairo - a vector graphics library with display and print output
00002  *
00003  * Copyright © 2003 University of Southern California
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it either under the terms of the GNU Lesser General Public
00007  * License version 2.1 as published by the Free Software Foundation
00008  * (the "LGPL") or, at your option, under the terms of the Mozilla
00009  * Public License Version 1.1 (the "MPL"). If you do not alter this
00010  * notice, a recipient may use your version of this file under either
00011  * the MPL or the LGPL.
00012  *
00013  * You should have received a copy of the LGPL along with this library
00014  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00016  * You should have received a copy of the MPL along with this library
00017  * in the file COPYING-MPL-1.1
00018  *
00019  * The contents of this file are subject to the Mozilla Public License
00020  * Version 1.1 (the "License"); you may not use this file except in
00021  * compliance with the License. You may obtain a copy of the License at
00022  * http://www.mozilla.org/MPL/
00023  *
00024  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
00025  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
00026  * the specific language governing rights and limitations.
00027  *
00028  * The Original Code is the cairo graphics library.
00029  *
00030  * The Initial Developer of the Original Code is University of Southern
00031  * California.
00032  *
00033  * Contributor(s):
00034  *     Carl D. Worth <cworth@cworth.org>
00035  *     Kristian Høgsberg <krh@redhat.com>
00036  */
00037 
00038 #include <png.h>
00039 #include <errno.h>
00040 #include "cairoint.h"
00041 
00042 /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
00043 static void
00044 unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
00045 {
00046     int i;
00047 
00048     for (i = 0; i < row_info->rowbytes; i += 4) {
00049         uint8_t *b = &data[i];
00050         uint32_t pixel;
00051         uint8_t  alpha;
00052 
00053        memcpy (&pixel, b, sizeof (uint32_t));
00054        alpha = (pixel & 0xff000000) >> 24;
00055         if (alpha == 0) {
00056            b[0] = b[1] = b[2] = b[3] = 0;
00057        } else {
00058             b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
00059             b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
00060             b[2] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
00061            b[3] = alpha;
00062        }
00063     }
00064 }
00065 
00066 /* Converts native endian xRGB => RGBx bytes */
00067 static void
00068 convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
00069 {
00070     int i;
00071 
00072     for (i = 0; i < row_info->rowbytes; i += 4) {
00073         uint8_t *b = &data[i];
00074         uint32_t pixel;
00075 
00076        memcpy (&pixel, b, sizeof (uint32_t));
00077        
00078        b[0] = (pixel & 0xff0000) >> 16;
00079        b[1] = (pixel & 0x00ff00) >>  8;
00080        b[2] = (pixel & 0x0000ff) >>  0;
00081        b[3] = 0;
00082     }
00083 }
00084 
00085 static cairo_status_t
00086 write_png (cairo_surface_t  *surface,
00087           png_rw_ptr        write_func,
00088           void                     *closure)
00089 {
00090     int i;
00091     cairo_status_t status = CAIRO_STATUS_SUCCESS;
00092     cairo_image_surface_t *image;
00093     void *image_extra;
00094     png_struct *png;
00095     png_info *info;
00096     png_time pt;
00097     png_byte **rows;
00098     png_color_16 white;
00099     int png_color_type;
00100     int depth;
00101 
00102     status = _cairo_surface_acquire_source_image (surface,
00103                                             &image,
00104                                             &image_extra);
00105 
00106     if (status == CAIRO_STATUS_NO_MEMORY)
00107         return CAIRO_STATUS_NO_MEMORY;
00108     else if (status != CAIRO_STATUS_SUCCESS)
00109        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
00110 
00111     rows = malloc (image->height * sizeof(png_byte*));
00112     if (rows == NULL) {
00113         status = CAIRO_STATUS_NO_MEMORY;
00114        goto BAIL1;
00115     }
00116 
00117     for (i = 0; i < image->height; i++)
00118        rows[i] = (png_byte *) image->data + i * image->stride;
00119 
00120     png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00121     if (png == NULL) {
00122        status = CAIRO_STATUS_NO_MEMORY;
00123        goto BAIL2;
00124     }
00125 
00126     info = png_create_info_struct (png);
00127     if (info == NULL) {
00128        status = CAIRO_STATUS_NO_MEMORY;
00129        goto BAIL3;
00130     }
00131 
00132     if (setjmp (png_jmpbuf (png))) {
00133        status = CAIRO_STATUS_NO_MEMORY;
00134        goto BAIL3;
00135     }
00136     
00137     png_set_write_fn (png, closure, write_func, NULL);
00138 
00139     switch (image->format) {
00140     case CAIRO_FORMAT_ARGB32:
00141        depth = 8;
00142        png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
00143        break;
00144     case CAIRO_FORMAT_RGB24:
00145        depth = 8;
00146        png_color_type = PNG_COLOR_TYPE_RGB;
00147        break;
00148     case CAIRO_FORMAT_A8:
00149        depth = 8;
00150        png_color_type = PNG_COLOR_TYPE_GRAY;
00151        break;
00152     case CAIRO_FORMAT_A1:
00153        depth = 1;
00154        png_color_type = PNG_COLOR_TYPE_GRAY;
00155        break;
00156     default:
00157        status = CAIRO_STATUS_NULL_POINTER;
00158        goto BAIL3;
00159     }
00160 
00161     png_set_IHDR (png, info,
00162                 image->width,
00163                 image->height, depth,
00164                 png_color_type,
00165                 PNG_INTERLACE_NONE,
00166                 PNG_COMPRESSION_TYPE_DEFAULT,
00167                 PNG_FILTER_TYPE_DEFAULT);
00168 
00169     white.red = 0xff;
00170     white.blue = 0xff;
00171     white.green = 0xff;
00172     png_set_bKGD (png, info, &white);
00173 
00174     png_convert_from_time_t (&pt, time (NULL));
00175     png_set_tIME (png, info, &pt);
00176 
00177     /* We have to call png_write_info() before setting up the write
00178      * transformation, since it stores data internally in 'png'
00179      * that is needed for the write transformation functions to work.
00180      */
00181     png_write_info (png, info);
00182     
00183     if (image->format == CAIRO_FORMAT_ARGB32)
00184        png_set_write_user_transform_fn (png, unpremultiply_data);
00185     else if (image->format == CAIRO_FORMAT_RGB24)
00186        png_set_write_user_transform_fn (png, convert_data_to_bytes);
00187     if (image->format == CAIRO_FORMAT_RGB24)
00188        png_set_filler (png, 0, PNG_FILLER_AFTER);
00189        
00190     png_write_image (png, rows);
00191     png_write_end (png, info);
00192 
00193 BAIL3:
00194     png_destroy_write_struct (&png, &info);
00195 BAIL2:
00196     free (rows);
00197 BAIL1:
00198     _cairo_surface_release_source_image (surface, image, image_extra);
00199 
00200     return status;
00201 }
00202 
00203 static void
00204 stdio_write_func (png_structp png, png_bytep data, png_size_t size)
00205 {
00206     FILE *fp;
00207 
00208     fp = png_get_io_ptr (png);
00209     if (fwrite (data, 1, size, fp) != size)
00210        png_error(png, "Write Error");
00211 }
00212 
00228 cairo_status_t
00229 cairo_surface_write_to_png (cairo_surface_t      *surface,
00230                          const char              *filename)
00231 {
00232     FILE *fp;
00233     cairo_status_t status;
00234 
00235     fp = fopen (filename, "wb");
00236     if (fp == NULL)
00237        return CAIRO_STATUS_WRITE_ERROR;
00238   
00239     status = write_png (surface, stdio_write_func, fp);
00240 
00241     if (fclose (fp) && status == CAIRO_STATUS_SUCCESS)
00242        status = CAIRO_STATUS_WRITE_ERROR;
00243 
00244     return status;
00245 }
00246 
00247 struct png_write_closure_t {
00248     cairo_write_func_t      write_func;
00249     void                    *closure;
00250 };
00251 
00252 static void
00253 stream_write_func (png_structp png, png_bytep data, png_size_t size)
00254 {
00255     cairo_status_t status;
00256     struct png_write_closure_t *png_closure;
00257 
00258     png_closure = png_get_io_ptr (png);
00259     status = png_closure->write_func (png_closure->closure, data, size);
00260     if (status)
00261        png_error(png, "Write Error");
00262 }
00263 
00278 cairo_status_t
00279 cairo_surface_write_to_png_stream (cairo_surface_t      *surface,
00280                                cairo_write_func_t       write_func,
00281                                void                     *closure)
00282 {
00283     struct png_write_closure_t png_closure;
00284 
00285     png_closure.write_func = write_func;
00286     png_closure.closure = closure;
00287 
00288     return write_png (surface, stream_write_func, &png_closure);
00289 }                                
00290 
00291 static INLINE int
00292 multiply_alpha (int alpha, int color)
00293 {
00294     int temp = (alpha * color) + 0x80;
00295     return ((temp + (temp >> 8)) >> 8);
00296 }
00297 
00298 /* Premultiplies data and converts RGBA bytes => native endian */
00299 static void
00300 premultiply_data (png_structp   png,
00301                   png_row_infop row_info,
00302                   png_bytep     data)
00303 {
00304     int i;
00305 
00306     for (i = 0; i < row_info->rowbytes; i += 4) {
00307        uint8_t *base = &data[i];
00308        uint8_t  alpha = base[3];
00309        uint32_t p;
00310 
00311        if (alpha == 0) {
00312            p = 0;
00313        } else {
00314            uint8_t  red = base[0];
00315            uint8_t  green = base[1];
00316            uint8_t  blue = base[2];
00317 
00318            if (alpha != 0xff) {
00319               red = multiply_alpha (alpha, red);
00320               green = multiply_alpha (alpha, green);
00321               blue = multiply_alpha (alpha, blue);
00322            }
00323            p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
00324        }
00325        memcpy (base, &p, sizeof (uint32_t));
00326     }
00327 }
00328 
00329 static cairo_surface_t *
00330 read_png (png_rw_ptr read_func,
00331          void        *closure)
00332 {
00333     cairo_surface_t *surface = (cairo_surface_t*) &_cairo_surface_nil;
00334     png_byte *data = NULL;
00335     int i;
00336     png_struct *png = NULL;
00337     png_info *info;
00338     png_uint_32 png_width, png_height, stride;
00339     int depth, color_type, interlace;
00340     unsigned int pixel_size;
00341     png_byte **row_pointers = NULL;
00342 
00343     /* XXX: Perhaps we'll want some other error handlers? */
00344     png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
00345                                   NULL,
00346                                   NULL,
00347                                   NULL);
00348     if (png == NULL)
00349        goto BAIL;
00350 
00351     info = png_create_info_struct (png);
00352     if (info == NULL)
00353        goto BAIL;
00354 
00355     png_set_read_fn (png, closure, read_func);
00356 
00357     if (setjmp (png_jmpbuf (png))) {
00358        surface = (cairo_surface_t*) &_cairo_surface_nil_read_error;
00359        goto BAIL;
00360     }
00361 
00362     png_read_info (png, info);
00363 
00364     png_get_IHDR (png, info,
00365                   &png_width, &png_height, &depth,
00366                   &color_type, &interlace, NULL, NULL);
00367     stride = 4 * png_width;
00368 
00369     /* convert palette/gray image to rgb */
00370     if (color_type == PNG_COLOR_TYPE_PALETTE)
00371         png_set_palette_to_rgb (png);
00372 
00373     /* expand gray bit depth if needed */
00374     if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8)
00375         png_set_gray_1_2_4_to_8 (png);
00376     /* transform transparency to alpha */
00377     if (png_get_valid(png, info, PNG_INFO_tRNS))
00378         png_set_tRNS_to_alpha (png);
00379 
00380     if (depth == 16)
00381         png_set_strip_16 (png);
00382 
00383     if (depth < 8)
00384         png_set_packing (png);
00385 
00386     /* convert grayscale to RGB */
00387     if (color_type == PNG_COLOR_TYPE_GRAY
00388         || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
00389         png_set_gray_to_rgb (png);
00390 
00391     if (interlace != PNG_INTERLACE_NONE)
00392         png_set_interlace_handling (png);
00393 
00394     png_set_filler (png, 0xff, PNG_FILLER_AFTER);
00395 
00396     png_set_read_user_transform_fn (png, premultiply_data);
00397 
00398     png_read_update_info (png, info);
00399 
00400     pixel_size = 4;
00401     data = malloc (png_width * png_height * pixel_size);
00402     if (data == NULL)
00403        goto BAIL;
00404 
00405     row_pointers = malloc (png_height * sizeof(char *));
00406     if (row_pointers == NULL)
00407        goto BAIL;
00408 
00409     for (i = 0; i < png_height; i++)
00410         row_pointers[i] = &data[i * png_width * pixel_size];
00411 
00412     png_read_image (png, row_pointers);
00413     png_read_end (png, info);
00414 
00415     surface = cairo_image_surface_create_for_data (data,
00416                                              CAIRO_FORMAT_ARGB32,
00417                                              png_width, png_height, stride);
00418     if (surface->status)
00419        goto BAIL;
00420 
00421     _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface);
00422     data = NULL;
00423 
00424  BAIL:
00425     if (row_pointers)
00426        free (row_pointers);
00427     if (data)
00428        free (data);
00429     if (png)
00430        png_destroy_read_struct (&png, &info, NULL);
00431 
00432     if (surface->status)
00433        _cairo_error (surface->status);
00434 
00435     return surface;
00436 }
00437 
00438 static void
00439 stdio_read_func (png_structp png, png_bytep data, png_size_t size)
00440 {
00441     FILE *fp;
00442 
00443     fp = png_get_io_ptr (png);
00444     if (fread (data, 1, size, fp) != size)
00445        png_error(png, "Read Error");
00446 }
00447 
00464 cairo_surface_t *
00465 cairo_image_surface_create_from_png (const char *filename)
00466 {
00467     FILE *fp;
00468     cairo_surface_t *surface;
00469 
00470     fp = fopen (filename, "rb");
00471     if (fp == NULL) {
00472        switch (errno) {
00473        case ENOMEM:
00474            _cairo_error (CAIRO_STATUS_NO_MEMORY);
00475            return (cairo_surface_t*) &_cairo_surface_nil;
00476        case ENOENT:
00477            _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
00478            return (cairo_surface_t*) &_cairo_surface_nil_file_not_found;
00479        default:
00480            _cairo_error (CAIRO_STATUS_READ_ERROR);
00481            return (cairo_surface_t*) &_cairo_surface_nil_read_error;
00482        }
00483     }
00484   
00485     surface = read_png (stdio_read_func, fp);
00486 
00487     fclose (fp);
00488 
00489     return surface;
00490 }
00491 
00492 struct png_read_closure_t {
00493     cairo_read_func_t       read_func;
00494     void                    *closure;
00495 };
00496 
00497 static void
00498 stream_read_func (png_structp png, png_bytep data, png_size_t size)
00499 {
00500     cairo_status_t status;
00501     struct png_read_closure_t *png_closure;
00502 
00503     png_closure = png_get_io_ptr (png);
00504     status = png_closure->read_func (png_closure->closure, data, size);
00505     if (status)
00506        png_error(png, "Read Error");
00507 }
00508 
00521 cairo_surface_t *
00522 cairo_image_surface_create_from_png_stream (cairo_read_func_t  read_func,
00523                                        void             *closure)
00524 {
00525     struct png_read_closure_t png_closure;
00526 
00527     png_closure.read_func = read_func;
00528     png_closure.closure = closure;
00529 
00530     return read_png (stream_read_func, &png_closure);
00531 }
00532