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