Home | History | Annotate | Download | only in sfnt
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  pngshim.c                                                              */
      4 /*                                                                         */
      5 /*    PNG Bitmap glyph support.                                            */
      6 /*                                                                         */
      7 /*  Copyright 2013-2018 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 #if defined( TT_CONFIG_OPTION_EMBEDDED_BITMAPS ) && \
     28     defined( FT_CONFIG_OPTION_USE_PNG )
     29 
     30   /* We always include <setjmp.h>, so make libpng shut up! */
     31 #define PNG_SKIP_SETJMP_CHECK 1
     32 #include <png.h>
     33 #include "pngshim.h"
     34 
     35 #include "sferrors.h"
     36 
     37 
     38   /* This code is freely based on cairo-png.c.  There's so many ways */
     39   /* to call libpng, and the way cairo does it is defacto standard.  */
     40 
     41   static unsigned int
     42   multiply_alpha( unsigned int  alpha,
     43                   unsigned int  color )
     44   {
     45     unsigned int  temp = alpha * color + 0x80;
     46 
     47 
     48     return ( temp + ( temp >> 8 ) ) >> 8;
     49   }
     50 
     51 
     52   /* Premultiplies data and converts RGBA bytes => BGRA. */
     53   static void
     54   premultiply_data( png_structp    png,
     55                     png_row_infop  row_info,
     56                     png_bytep      data )
     57   {
     58     unsigned int  i = 0, limit;
     59 
     60     /* The `vector_size' attribute was introduced in gcc 3.1, which */
     61     /* predates clang; the `__BYTE_ORDER__' preprocessor symbol was */
     62     /* introduced in gcc 4.6 and clang 3.2, respectively.           */
     63     /* `__builtin_shuffle' for gcc was introduced in gcc 4.7.0.     */
     64 #if ( ( defined( __GNUC__ )                                &&             \
     65         ( ( __GNUC__ >= 5 )                              ||               \
     66         ( ( __GNUC__ == 4 ) && ( __GNUC_MINOR__ >= 7 ) ) ) )         ||   \
     67       ( defined( __clang__ )                                       &&     \
     68         ( ( __clang_major__ >= 4 )                               ||       \
     69         ( ( __clang_major__ == 3 ) && ( __clang_minor__ >= 2 ) ) ) ) ) && \
     70     defined( __OPTIMIZE__ )                                            && \
     71     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
     72 
     73 #ifdef __clang__
     74     /* the clang documentation doesn't cover the two-argument case of */
     75     /* `__builtin_shufflevector'; however, it is is implemented since */
     76     /* version 2.8                                                    */
     77 #define vector_shuffle  __builtin_shufflevector
     78 #else
     79 #define vector_shuffle  __builtin_shuffle
     80 #endif
     81 
     82     typedef unsigned short  v82 __attribute__(( vector_size( 16 ) ));
     83 
     84 
     85     if ( row_info->rowbytes > 15 )
     86     {
     87       /* process blocks of 16 bytes in one rush, which gives a nice speed-up */
     88       limit = row_info->rowbytes - 16 + 1;
     89       for ( ; i < limit; i += 16 )
     90       {
     91         unsigned char*  base = &data[i];
     92 
     93         v82  s, s0, s1, a;
     94 
     95         /* clang <= 3.9 can't apply scalar values to vectors */
     96         /* (or rather, it needs a different syntax)          */
     97         v82  n0x80 = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 };
     98         v82  n0xFF = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
     99         v82  n8    = { 8, 8, 8, 8, 8, 8, 8, 8 };
    100 
    101         v82  ma = { 1, 1, 3, 3, 5, 5, 7, 7 };
    102         v82  o1 = { 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF };
    103         v82  m0 = { 1, 0, 3, 2, 5, 4, 7, 6 };
    104 
    105 
    106         ft_memcpy( &s, base, 16 );            /* RGBA RGBA RGBA RGBA */
    107         s0 = s & n0xFF;                       /*  R B  R B  R B  R B */
    108         s1 = s >> n8;                         /*  G A  G A  G A  G A */
    109 
    110         a   = vector_shuffle( s1, ma );       /*  A A  A A  A A  A A */
    111         s1 |= o1;                             /*  G 1  G 1  G 1  G 1 */
    112         s0  = vector_shuffle( s0, m0 );       /*  B R  B R  B R  B R */
    113 
    114         s0 *= a;
    115         s1 *= a;
    116         s0 += n0x80;
    117         s1 += n0x80;
    118         s0  = ( s0 + ( s0 >> n8 ) ) >> n8;
    119         s1  = ( s1 + ( s1 >> n8 ) ) >> n8;
    120 
    121         s = s0 | ( s1 << n8 );
    122         ft_memcpy( base, &s, 16 );
    123       }
    124     }
    125 #endif /* use `vector_size' */
    126 
    127     FT_UNUSED( png );
    128 
    129     limit = row_info->rowbytes;
    130     for ( ; i < limit; i += 4 )
    131     {
    132       unsigned char*  base  = &data[i];
    133       unsigned int    alpha = base[3];
    134 
    135 
    136       if ( alpha == 0 )
    137         base[0] = base[1] = base[2] = base[3] = 0;
    138 
    139       else
    140       {
    141         unsigned int  red   = base[0];
    142         unsigned int  green = base[1];
    143         unsigned int  blue  = base[2];
    144 
    145 
    146         if ( alpha != 0xFF )
    147         {
    148           red   = multiply_alpha( alpha, red   );
    149           green = multiply_alpha( alpha, green );
    150           blue  = multiply_alpha( alpha, blue  );
    151         }
    152 
    153         base[0] = (unsigned char)blue;
    154         base[1] = (unsigned char)green;
    155         base[2] = (unsigned char)red;
    156         base[3] = (unsigned char)alpha;
    157       }
    158     }
    159   }
    160 
    161 
    162   /* Converts RGBx bytes to BGRA. */
    163   static void
    164   convert_bytes_to_data( png_structp    png,
    165                          png_row_infop  row_info,
    166                          png_bytep      data )
    167   {
    168     unsigned int  i;
    169 
    170     FT_UNUSED( png );
    171 
    172 
    173     for ( i = 0; i < row_info->rowbytes; i += 4 )
    174     {
    175       unsigned char*  base  = &data[i];
    176       unsigned int    red   = base[0];
    177       unsigned int    green = base[1];
    178       unsigned int    blue  = base[2];
    179 
    180 
    181       base[0] = (unsigned char)blue;
    182       base[1] = (unsigned char)green;
    183       base[2] = (unsigned char)red;
    184       base[3] = 0xFF;
    185     }
    186   }
    187 
    188 
    189   /* Use error callback to avoid png writing to stderr. */
    190   static void
    191   error_callback( png_structp      png,
    192                   png_const_charp  error_msg )
    193   {
    194     FT_Error*  error = (FT_Error*)png_get_error_ptr( png );
    195 
    196     FT_UNUSED( error_msg );
    197 
    198 
    199     *error = FT_THROW( Out_Of_Memory );
    200 #ifdef PNG_SETJMP_SUPPORTED
    201     ft_longjmp( png_jmpbuf( png ), 1 );
    202 #endif
    203     /* if we get here, then we have no choice but to abort ... */
    204   }
    205 
    206 
    207   /* Use warning callback to avoid png writing to stderr. */
    208   static void
    209   warning_callback( png_structp      png,
    210                     png_const_charp  error_msg )
    211   {
    212     FT_UNUSED( png );
    213     FT_UNUSED( error_msg );
    214 
    215     /* Just ignore warnings. */
    216   }
    217 
    218 
    219   static void
    220   read_data_from_FT_Stream( png_structp  png,
    221                             png_bytep    data,
    222                             png_size_t   length )
    223   {
    224     FT_Error   error;
    225     png_voidp  p      = png_get_io_ptr( png );
    226     FT_Stream  stream = (FT_Stream)p;
    227 
    228 
    229     if ( FT_FRAME_ENTER( length ) )
    230     {
    231       FT_Error*  e = (FT_Error*)png_get_error_ptr( png );
    232 
    233 
    234       *e = FT_THROW( Invalid_Stream_Read );
    235       png_error( png, NULL );
    236 
    237       return;
    238     }
    239 
    240     ft_memcpy( data, stream->cursor, length );
    241 
    242     FT_FRAME_EXIT();
    243   }
    244 
    245 
    246   FT_LOCAL_DEF( FT_Error )
    247   Load_SBit_Png( FT_GlyphSlot     slot,
    248                  FT_Int           x_offset,
    249                  FT_Int           y_offset,
    250                  FT_Int           pix_bits,
    251                  TT_SBit_Metrics  metrics,
    252                  FT_Memory        memory,
    253                  FT_Byte*         data,
    254                  FT_UInt          png_len,
    255                  FT_Bool          populate_map_and_metrics,
    256                  FT_Bool          metrics_only )
    257   {
    258     FT_Bitmap    *map   = &slot->bitmap;
    259     FT_Error      error = FT_Err_Ok;
    260     FT_StreamRec  stream;
    261 
    262     png_structp  png;
    263     png_infop    info;
    264     png_uint_32  imgWidth, imgHeight;
    265 
    266     int         bitdepth, color_type, interlace;
    267     FT_Int      i;
    268     png_byte*  *rows = NULL; /* pacify compiler */
    269 
    270 
    271     if ( x_offset < 0 ||
    272          y_offset < 0 )
    273     {
    274       error = FT_THROW( Invalid_Argument );
    275       goto Exit;
    276     }
    277 
    278     if ( !populate_map_and_metrics                            &&
    279          ( (FT_UInt)x_offset + metrics->width  > map->width ||
    280            (FT_UInt)y_offset + metrics->height > map->rows  ||
    281            pix_bits != 32                                   ||
    282            map->pixel_mode != FT_PIXEL_MODE_BGRA            ) )
    283     {
    284       error = FT_THROW( Invalid_Argument );
    285       goto Exit;
    286     }
    287 
    288     FT_Stream_OpenMemory( &stream, data, png_len );
    289 
    290     png = png_create_read_struct( PNG_LIBPNG_VER_STRING,
    291                                   &error,
    292                                   error_callback,
    293                                   warning_callback );
    294     if ( !png )
    295     {
    296       error = FT_THROW( Out_Of_Memory );
    297       goto Exit;
    298     }
    299 
    300     info = png_create_info_struct( png );
    301     if ( !info )
    302     {
    303       error = FT_THROW( Out_Of_Memory );
    304       png_destroy_read_struct( &png, NULL, NULL );
    305       goto Exit;
    306     }
    307 
    308     if ( ft_setjmp( png_jmpbuf( png ) ) )
    309     {
    310       error = FT_THROW( Invalid_File_Format );
    311       goto DestroyExit;
    312     }
    313 
    314     png_set_read_fn( png, &stream, read_data_from_FT_Stream );
    315 
    316     png_read_info( png, info );
    317     png_get_IHDR( png, info,
    318                   &imgWidth, &imgHeight,
    319                   &bitdepth, &color_type, &interlace,
    320                   NULL, NULL );
    321 
    322     if ( error                                        ||
    323          ( !populate_map_and_metrics                &&
    324            ( (FT_Int)imgWidth  != metrics->width  ||
    325              (FT_Int)imgHeight != metrics->height ) ) )
    326       goto DestroyExit;
    327 
    328     if ( populate_map_and_metrics )
    329     {
    330       metrics->width  = (FT_UShort)imgWidth;
    331       metrics->height = (FT_UShort)imgHeight;
    332 
    333       map->width      = metrics->width;
    334       map->rows       = metrics->height;
    335       map->pixel_mode = FT_PIXEL_MODE_BGRA;
    336       map->pitch      = (int)( map->width * 4 );
    337       map->num_grays  = 256;
    338 
    339       /* reject too large bitmaps similarly to the rasterizer */
    340       if ( map->rows > 0x7FFF || map->width > 0x7FFF )
    341       {
    342         error = FT_THROW( Array_Too_Large );
    343         goto DestroyExit;
    344       }
    345     }
    346 
    347     /* convert palette/gray image to rgb */
    348     if ( color_type == PNG_COLOR_TYPE_PALETTE )
    349       png_set_palette_to_rgb( png );
    350 
    351     /* expand gray bit depth if needed */
    352     if ( color_type == PNG_COLOR_TYPE_GRAY )
    353     {
    354 #if PNG_LIBPNG_VER >= 10209
    355       png_set_expand_gray_1_2_4_to_8( png );
    356 #else
    357       png_set_gray_1_2_4_to_8( png );
    358 #endif
    359     }
    360 
    361     /* transform transparency to alpha */
    362     if ( png_get_valid(png, info, PNG_INFO_tRNS ) )
    363       png_set_tRNS_to_alpha( png );
    364 
    365     if ( bitdepth == 16 )
    366       png_set_strip_16( png );
    367 
    368     if ( bitdepth < 8 )
    369       png_set_packing( png );
    370 
    371     /* convert grayscale to RGB */
    372     if ( color_type == PNG_COLOR_TYPE_GRAY       ||
    373          color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
    374       png_set_gray_to_rgb( png );
    375 
    376     if ( interlace != PNG_INTERLACE_NONE )
    377       png_set_interlace_handling( png );
    378 
    379     png_set_filler( png, 0xFF, PNG_FILLER_AFTER );
    380 
    381     /* recheck header after setting EXPAND options */
    382     png_read_update_info(png, info );
    383     png_get_IHDR( png, info,
    384                   &imgWidth, &imgHeight,
    385                   &bitdepth, &color_type, &interlace,
    386                   NULL, NULL );
    387 
    388     if ( bitdepth != 8                              ||
    389         !( color_type == PNG_COLOR_TYPE_RGB       ||
    390            color_type == PNG_COLOR_TYPE_RGB_ALPHA ) )
    391     {
    392       error = FT_THROW( Invalid_File_Format );
    393       goto DestroyExit;
    394     }
    395 
    396     if ( metrics_only )
    397       goto DestroyExit;
    398 
    399     switch ( color_type )
    400     {
    401     default:
    402       /* Shouldn't happen, but fall through. */
    403 
    404     case PNG_COLOR_TYPE_RGB_ALPHA:
    405       png_set_read_user_transform_fn( png, premultiply_data );
    406       break;
    407 
    408     case PNG_COLOR_TYPE_RGB:
    409       /* Humm, this smells.  Carry on though. */
    410       png_set_read_user_transform_fn( png, convert_bytes_to_data );
    411       break;
    412     }
    413 
    414     if ( populate_map_and_metrics )
    415     {
    416       /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */
    417       FT_ULong  size = map->rows * (FT_ULong)map->pitch;
    418 
    419 
    420       error = ft_glyphslot_alloc_bitmap( slot, size );
    421       if ( error )
    422         goto DestroyExit;
    423     }
    424 
    425     if ( FT_NEW_ARRAY( rows, imgHeight ) )
    426     {
    427       error = FT_THROW( Out_Of_Memory );
    428       goto DestroyExit;
    429     }
    430 
    431     for ( i = 0; i < (FT_Int)imgHeight; i++ )
    432       rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4;
    433 
    434     png_read_image( png, rows );
    435 
    436     FT_FREE( rows );
    437 
    438     png_read_end( png, info );
    439 
    440   DestroyExit:
    441     png_destroy_read_struct( &png, &info, NULL );
    442     FT_Stream_Close( &stream );
    443 
    444   Exit:
    445     return error;
    446   }
    447 
    448 #else /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
    449 
    450   /* ANSI C doesn't like empty source files */
    451   typedef int  _pngshim_dummy;
    452 
    453 #endif /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
    454 
    455 
    456 /* END */
    457