Home | History | Annotate | Download | only in sfnt
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  pngshim.c                                                              */
      4 /*                                                                         */
      5 /*    PNG Bitmap glyph support.                                            */
      6 /*                                                                         */
      7 /*  Copyright 2013, 2014 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 = (FT_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     ft_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 = (FT_Error*)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   FT_LOCAL_DEF( FT_Error )
    178   Load_SBit_Png( FT_GlyphSlot     slot,
    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                  FT_Bool          populate_map_and_metrics )
    187   {
    188     FT_Bitmap    *map   = &slot->bitmap;
    189     FT_Error      error = FT_Err_Ok;
    190     FT_StreamRec  stream;
    191 
    192     png_structp  png;
    193     png_infop    info;
    194     png_uint_32  imgWidth, imgHeight;
    195 
    196     int         bitdepth, color_type, interlace;
    197     FT_Int      i;
    198     png_byte*  *rows = NULL; /* pacify compiler */
    199 
    200 
    201     if ( x_offset < 0 ||
    202          y_offset < 0 )
    203     {
    204       error = FT_THROW( Invalid_Argument );
    205       goto Exit;
    206     }
    207 
    208     if ( !populate_map_and_metrics                   &&
    209          ( x_offset + metrics->width  > map->width ||
    210            y_offset + metrics->height > map->rows  ||
    211            pix_bits != 32                          ||
    212            map->pixel_mode != FT_PIXEL_MODE_BGRA   ) )
    213     {
    214       error = FT_THROW( Invalid_Argument );
    215       goto Exit;
    216     }
    217 
    218     FT_Stream_OpenMemory( &stream, data, png_len );
    219 
    220     png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
    221                                   &error,
    222                                   error_callback,
    223                                   warning_callback );
    224     if ( !png )
    225     {
    226       error = FT_THROW( Out_Of_Memory );
    227       goto Exit;
    228     }
    229 
    230     info = png_create_info_struct( png );
    231     if ( !info )
    232     {
    233       error = FT_THROW( Out_Of_Memory );
    234       png_destroy_read_struct( &png, NULL, NULL );
    235       goto Exit;
    236     }
    237 
    238     if ( ft_setjmp( png_jmpbuf( png ) ) )
    239     {
    240       error = FT_THROW( Invalid_File_Format );
    241       goto DestroyExit;
    242     }
    243 
    244     png_set_read_fn( png, &stream, read_data_from_FT_Stream );
    245 
    246     png_read_info( png, info );
    247     png_get_IHDR( png, info,
    248                   &imgWidth, &imgHeight,
    249                   &bitdepth, &color_type, &interlace,
    250                   NULL, NULL );
    251 
    252     if ( error                                        ||
    253          ( !populate_map_and_metrics                &&
    254            ( (FT_Int)imgWidth  != metrics->width  ||
    255              (FT_Int)imgHeight != metrics->height ) ) )
    256       goto DestroyExit;
    257 
    258     if ( populate_map_and_metrics )
    259     {
    260       FT_Long  size;
    261 
    262 
    263       metrics->width  = (FT_Int)imgWidth;
    264       metrics->height = (FT_Int)imgHeight;
    265 
    266       map->width      = metrics->width;
    267       map->rows       = metrics->height;
    268       map->pixel_mode = FT_PIXEL_MODE_BGRA;
    269       map->pitch      = map->width * 4;
    270       map->num_grays  = 256;
    271 
    272       size = map->rows * map->pitch;
    273 
    274       error = ft_glyphslot_alloc_bitmap( slot, size );
    275       if ( error )
    276         goto DestroyExit;
    277     }
    278 
    279     /* convert palette/gray image to rgb */
    280     if ( color_type == PNG_COLOR_TYPE_PALETTE )
    281       png_set_palette_to_rgb( png );
    282 
    283     /* expand gray bit depth if needed */
    284     if ( color_type == PNG_COLOR_TYPE_GRAY )
    285     {
    286 #if PNG_LIBPNG_VER >= 10209
    287       png_set_expand_gray_1_2_4_to_8( png );
    288 #else
    289       png_set_gray_1_2_4_to_8( png );
    290 #endif
    291     }
    292 
    293     /* transform transparency to alpha */
    294     if ( png_get_valid(png, info, PNG_INFO_tRNS ) )
    295       png_set_tRNS_to_alpha( png );
    296 
    297     if ( bitdepth == 16 )
    298       png_set_strip_16( png );
    299 
    300     if ( bitdepth < 8 )
    301       png_set_packing( png );
    302 
    303     /* convert grayscale to RGB */
    304     if ( color_type == PNG_COLOR_TYPE_GRAY       ||
    305          color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
    306       png_set_gray_to_rgb( png );
    307 
    308     if ( interlace != PNG_INTERLACE_NONE )
    309       png_set_interlace_handling( png );
    310 
    311     png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
    312 
    313     /* recheck header after setting EXPAND options */
    314     png_read_update_info(png, info );
    315     png_get_IHDR( png, info,
    316                   &imgWidth, &imgHeight,
    317                   &bitdepth, &color_type, &interlace,
    318                   NULL, NULL );
    319 
    320     if ( bitdepth != 8                              ||
    321         !( color_type == PNG_COLOR_TYPE_RGB       ||
    322            color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
    323     {
    324       error = FT_THROW( Invalid_File_Format );
    325       goto DestroyExit;
    326     }
    327 
    328     switch ( color_type )
    329     {
    330     default:
    331       /* Shouldn't happen, but fall through. */
    332 
    333     case PNG_COLOR_TYPE_RGB_ALPHA:
    334       png_set_read_user_transform_fn( png, premultiply_data );
    335       break;
    336 
    337     case PNG_COLOR_TYPE_RGB:
    338       /* Humm, this smells.  Carry on though. */
    339       png_set_read_user_transform_fn( png, convert_bytes_to_data );
    340       break;
    341     }
    342 
    343     if ( FT_NEW_ARRAY( rows, imgHeight ) )
    344     {
    345       error = FT_THROW( Out_Of_Memory );
    346       goto DestroyExit;
    347     }
    348 
    349     for ( i = 0; i < (FT_Int)imgHeight; i++ )
    350       rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
    351 
    352     png_read_image( png, rows );
    353 
    354     FT_FREE( rows );
    355 
    356     png_read_end( png, info );
    357 
    358   DestroyExit:
    359     png_destroy_read_struct( &png, &info, NULL );
    360     FT_Stream_Close( &stream );
    361 
    362   Exit:
    363     return error;
    364   }
    365 
    366 #endif /* FT_CONFIG_OPTION_USE_PNG */
    367 
    368 
    369 /* END */
    370