tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pngshim.c (13188B)


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