Home | History | Annotate | Download | only in sfnt
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  pngshim.c                                                              */
      4 /*                                                                         */
      5 /*    PNG Bitmap glyph support.                                            */
      6 /*                                                                         */
      7 /*  Copyright 2013 by Google, Inc.                                         */
      8 /*  Written by Stuart Gill and Behdad Esfahbod.                            */
      9 /*                                                                         */
     10 /*  This file is part of the FreeType project, and may only be used,       */
     11 /*  modified, and distributed under the terms of the FreeType project      */
     12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     13 /*  this file you indicate that you have read the license and              */
     14 /*  understand and accept it fully.                                        */
     15 /*                                                                         */
     16 /***************************************************************************/
     17 
     18 
     19 #include <ft2build.h>
     20 #include FT_INTERNAL_DEBUG_H
     21 #include FT_INTERNAL_STREAM_H
     22 #include FT_TRUETYPE_TAGS_H
     23 #include FT_CONFIG_STANDARD_LIBRARY_H
     24 
     25 
     26 #ifdef FT_CONFIG_OPTION_USE_PNG
     27 
     28   /* We always include <stjmp.h>, so make libpng shut up! */
     29 #define PNG_SKIP_SETJMP_CHECK 1
     30 #include <png.h>
     31 #include "pngshim.h"
     32 
     33 #include "sferrors.h"
     34 
     35 
     36   /* This code is freely based on cairo-png.c.  There's so many ways */
     37   /* to call libpng, and the way cairo does it is defacto standard.  */
     38 
     39   static int
     40   multiply_alpha( int  alpha,
     41                   int  color )
     42   {
     43     int  temp = ( alpha * color ) + 0x80;
     44 
     45 
     46     return ( temp + ( temp >> 8 ) ) >> 8;
     47   }
     48 
     49 
     50   /* Premultiplies data and converts RGBA bytes => native endian. */
     51   static void
     52   premultiply_data( png_structp    png,
     53                     png_row_infop  row_info,
     54                     png_bytep      data )
     55   {
     56     unsigned int  i;
     57 
     58     FT_UNUSED( png );
     59 
     60 
     61     for ( i = 0; i < row_info->rowbytes; i += 4 )
     62     {
     63       unsigned char*  base  = &data[i];
     64       unsigned int    alpha = base[3];
     65 
     66 
     67       if ( alpha == 0 )
     68         base[0] = base[1] = base[2] = base[3] = 0;
     69 
     70       else
     71       {
     72         unsigned int  red   = base[0];
     73         unsigned int  green = base[1];
     74         unsigned int  blue  = base[2];
     75 
     76 
     77         if ( alpha != 0xFF )
     78         {
     79           red   = multiply_alpha( alpha, red   );
     80           green = multiply_alpha( alpha, green );
     81           blue  = multiply_alpha( alpha, blue  );
     82         }
     83 
     84         base[0] = blue;
     85         base[1] = green;
     86         base[2] = red;
     87         base[3] = alpha;
     88       }
     89     }
     90   }
     91 
     92 
     93   /* Converts RGBx bytes to BGRA. */
     94   static void
     95   convert_bytes_to_data( png_structp    png,
     96                          png_row_infop  row_info,
     97                          png_bytep      data )
     98   {
     99     unsigned int  i;
    100 
    101     FT_UNUSED( png );
    102 
    103 
    104     for ( i = 0; i < row_info->rowbytes; i += 4 )
    105     {
    106       unsigned char*  base  = &data[i];
    107       unsigned int    red   = base[0];
    108       unsigned int    green = base[1];
    109       unsigned int    blue  = base[2];
    110 
    111 
    112       base[0] = blue;
    113       base[1] = green;
    114       base[2] = red;
    115       base[3] = 0xFF;
    116     }
    117   }
    118 
    119 
    120   /* Use error callback to avoid png writing to stderr. */
    121   static void
    122   error_callback( png_structp      png,
    123                   png_const_charp  error_msg )
    124   {
    125     FT_Error*  error = png_get_error_ptr( png );
    126 
    127     FT_UNUSED( error_msg );
    128 
    129 
    130     *error = FT_THROW( Out_Of_Memory );
    131 #ifdef PNG_SETJMP_SUPPORTED
    132     longjmp( png_jmpbuf( png ), 1 );
    133 #endif
    134     /* if we get here, then we have no choice but to abort ... */
    135   }
    136 
    137 
    138   /* Use warning callback to avoid png writing to stderr. */
    139   static void
    140   warning_callback( png_structp      png,
    141                     png_const_charp  error_msg )
    142   {
    143     FT_UNUSED( png );
    144     FT_UNUSED( error_msg );
    145 
    146     /* Just ignore warnings. */
    147   }
    148 
    149 
    150   static void
    151   read_data_from_FT_Stream( png_structp  png,
    152                             png_bytep    data,
    153                             png_size_t   length )
    154   {
    155     FT_Error   error;
    156     png_voidp  p      = png_get_io_ptr( png );
    157     FT_Stream  stream = (FT_Stream)p;
    158 
    159 
    160     if ( FT_FRAME_ENTER( length ) )
    161     {
    162       FT_Error*  e = png_get_error_ptr( png );
    163 
    164 
    165       *e = FT_THROW( Invalid_Stream_Read );
    166       png_error( png, NULL );
    167 
    168       return;
    169     }
    170 
    171     memcpy( data, stream->cursor, length );
    172 
    173     FT_FRAME_EXIT();
    174   }
    175 
    176 
    177   static FT_Error
    178   Load_SBit_Png( FT_Bitmap*       map,
    179                  FT_Int           x_offset,
    180                  FT_Int           y_offset,
    181                  FT_Int           pix_bits,
    182                  TT_SBit_Metrics  metrics,
    183                  FT_Memory        memory,
    184                  FT_Byte*         data,
    185                  FT_UInt          png_len )
    186   {
    187     FT_Error      error = FT_Err_Ok;
    188     FT_StreamRec  stream;
    189 
    190     png_structp  png;
    191     png_infop    info;
    192     png_uint_32  imgWidth, imgHeight;
    193 
    194     int         bitdepth, color_type, interlace;
    195     FT_Int      i;
    196     png_byte*  *rows;
    197 
    198 
    199     if ( x_offset < 0 || x_offset + metrics->width  > map->width ||
    200          y_offset < 0 || y_offset + metrics->height > map->rows  ||
    201          pix_bits != 32 || map->pixel_mode != FT_PIXEL_MODE_BGRA )
    202     {
    203       error = FT_THROW( Invalid_Argument );
    204       goto Exit;
    205     }
    206 
    207     FT_Stream_OpenMemory( &stream, data, png_len );
    208 
    209     png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
    210                                   &error,
    211                                   error_callback,
    212                                   warning_callback );
    213     if ( !png )
    214     {
    215       error = FT_THROW( Out_Of_Memory );
    216       goto Exit;
    217     }
    218 
    219     info = png_create_info_struct( png );
    220     if ( !info )
    221     {
    222       error = FT_THROW( Out_Of_Memory );
    223       png_destroy_read_struct( &png, NULL, NULL );
    224       goto Exit;
    225     }
    226 
    227     if ( ft_setjmp( png_jmpbuf( png ) ) )
    228     {
    229       error = FT_THROW( Invalid_File_Format );
    230       goto DestroyExit;
    231     }
    232 
    233     png_set_read_fn( png, &stream, read_data_from_FT_Stream );
    234 
    235     png_read_info( png, info );
    236     png_get_IHDR( png, info,
    237                   &imgWidth, &imgHeight,
    238                   &bitdepth, &color_type, &interlace,
    239                   NULL, NULL );
    240 
    241     if ( error != FT_Err_Ok                   ||
    242          (FT_Int)imgWidth  != metrics->width  ||
    243          (FT_Int)imgHeight != metrics->height )
    244       goto DestroyExit;
    245 
    246     /* convert palette/gray image to rgb */
    247     if ( color_type == PNG_COLOR_TYPE_PALETTE )
    248       png_set_palette_to_rgb( png );
    249 
    250     /* expand gray bit depth if needed */
    251     if ( color_type == PNG_COLOR_TYPE_GRAY )
    252     {
    253 #if PNG_LIBPNG_VER >= 10209
    254       png_set_expand_gray_1_2_4_to_8( png );
    255 #else
    256       png_set_gray_1_2_4_to_8( png );
    257 #endif
    258     }
    259 
    260     /* transform transparency to alpha */
    261     if ( png_get_valid(png, info, PNG_INFO_tRNS ) )
    262       png_set_tRNS_to_alpha( png );
    263 
    264     if ( bitdepth == 16 )
    265       png_set_strip_16( png );
    266 
    267     if ( bitdepth < 8 )
    268       png_set_packing( png );
    269 
    270     /* convert grayscale to RGB */
    271     if ( color_type == PNG_COLOR_TYPE_GRAY       ||
    272          color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
    273       png_set_gray_to_rgb( png );
    274 
    275     if ( interlace != PNG_INTERLACE_NONE )
    276       png_set_interlace_handling( png );
    277 
    278     png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
    279 
    280     /* recheck header after setting EXPAND options */
    281     png_read_update_info(png, info );
    282     png_get_IHDR( png, info,
    283                   &imgWidth, &imgHeight,
    284                   &bitdepth, &color_type, &interlace,
    285                   NULL, NULL );
    286 
    287     if ( bitdepth != 8                              ||
    288         !( color_type == PNG_COLOR_TYPE_RGB       ||
    289            color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
    290     {
    291       error = FT_THROW( Invalid_File_Format );
    292       goto DestroyExit;
    293     }
    294 
    295     switch ( color_type )
    296     {
    297     default:
    298       /* Shouldn't happen, but fall through. */
    299 
    300     case PNG_COLOR_TYPE_RGB_ALPHA:
    301       png_set_read_user_transform_fn( png, premultiply_data );
    302       break;
    303 
    304     case PNG_COLOR_TYPE_RGB:
    305       /* Humm, this smells.  Carry on though. */
    306       png_set_read_user_transform_fn( png, convert_bytes_to_data );
    307       break;
    308     }
    309 
    310     if ( FT_NEW_ARRAY( rows, imgHeight ) )
    311     {
    312       error = FT_THROW( Out_Of_Memory );
    313       goto DestroyExit;
    314     }
    315 
    316     for ( i = 0; i < (FT_Int)imgHeight; i++ )
    317       rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
    318 
    319     png_read_image( png, rows );
    320 
    321     FT_FREE( rows );
    322 
    323     png_read_end( png, info );
    324 
    325   DestroyExit:
    326     png_destroy_read_struct( &png, &info, NULL );
    327     FT_Stream_Close( &stream );
    328 
    329   Exit:
    330     return error;
    331   }
    332 
    333 #endif /* FT_CONFIG_OPTION_USE_PNG */
    334 
    335 
    336 /* END */
    337